GCC Code Coverage Report


Directory: src/solver/
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 83.1% 374 / 0 / 450
Functions: 100.0% 19 / 0 / 19
Branches: 59.2% 189 / 0 / 319

rain.c
Line Branch Exec Source
1 //-----------------------------------------------------------------------------
2 // rain.c
3 //
4 // Project: EPA SWMM5
5 // Version: 5.2
6 // Date: 11/01/21 (Build 5.2.0)
7 // Author: L. Rossman
8 //
9 // Places rainfall data from external files into a SWMM rainfall
10 // interface file.
11 //
12 // The following types of external data files are supported:
13 // NWS_TAPE: NCDC NWS TD 3240 or 3260 data in fixed field widths
14 // NWS_SPACE_DELIMITED: NCDC NWS TD (DSI) 3240 or 3260 data in space delimited
15 // format, with or without header lines, with or without
16 // station name
17 // NWS_COMMA_DELIMITED: NCDC NWS TD (DSI) 3240 or 3260 data in comma delimited
18 // format, with or without header lines
19 // NWS_ONLINE_60: NCDC NWS hourly space delimited online format
20 // NWS_ONLINE_15: NCDC NWS fifteen minute space delimited online format
21 // AES_HLY: Canadian AES hourly data with 3-digit year
22 // CMC_HLY: Canadian CMC hourly data in HLY03 or HLY21 format
23 // CMC_FIF: Canadian CMC fifteen minute data in in FIF21 format
24 // STD_SPACE_DELIMITED: standard SWMM space delimted format:
25 // StaID Year Month Day Hour Minute Rainfall
26 //
27 // The layout of the SWMM binary rainfall interface file is:
28 // File stamp ("SWMM5-RAIN") (10 bytes)
29 // Number of SWMM rain gages in file (4-byte int)
30 // Repeated for each rain gage:
31 // recording station ID (not SWMM rain gage ID) (MAXMSG+1 (=80) bytes)
32 // gage recording interval (seconds) (4-byte int)
33 // starting byte of rain data in file (4-byte int)
34 // ending byte+1 of rain data in file (4-byte int)
35 // For each gage:
36 // For each time period with non-zero rain:
37 // Date/time for start of period (8-byte double)
38 // Rain depth (inches) (4-byte float)
39 //
40 // Update History
41 // ==============
42 // Release 5.1.010:
43 // - Modified error message for records out of sequence in std. format file.
44 // Release 5.1.011:
45 // - Can now read decimal rainfall values in newer NWS online format.
46 // Release 5.1.013:
47 // - Variable x properly initialized with float value in readNwsOnlineValue().
48 // Release 5.1.014:
49 // - Fixed indexing bug in rainFileConflict() function.
50 //-----------------------------------------------------------------------------
51 #define _CRT_SECURE_NO_DEPRECATE
52
53 #include <stdlib.h>
54 #include <string.h>
55 #include "headers.h"
56
57 //-----------------------------------------------------------------------------
58 // Constants
59 //-----------------------------------------------------------------------------
60 enum RainFileFormat {UNKNOWN_FORMAT, NWS_TAPE, NWS_SPACE_DELIMITED,
61 NWS_COMMA_DELIMITED, NWS_ONLINE_60, NWS_ONLINE_15,
62 AES_HLY, CMC_HLY, CMC_FIF, STD_SPACE_DELIMITED};
63 enum ConditionCodes {NO_CONDITION, ACCUMULATED_PERIOD, DELETED_PERIOD,
64 MISSING_PERIOD};
65
66 //-----------------------------------------------------------------------------
67 // Shared variables
68 //-----------------------------------------------------------------------------
69 TRainStats RainStats; // see objects.h for definition
70 int Condition; // rainfall condition code
71 int TimeOffset; // time offset of rainfall reading (sec)
72 int DataOffset; // start of data on line of input
73 int ValueOffset; // start of rain value on input line
74 int RainType; // rain measurement type code
75 int Interval; // rain measurement interval (sec)
76 double UnitsFactor; // units conversion factor
77 float RainAccum; // rainfall depth accumulation
78 char *StationID; // station ID appearing in rain file
79 DateTime AccumStartDate; // date when accumulation begins
80 DateTime PreviousDate; // date of previous rainfall record
81 int GageIndex; // index of rain gage analyzed
82 int hasStationName; // true if data contains station name
83
84 //-----------------------------------------------------------------------------
85 // External functions (declared in funcs.h)
86 //-----------------------------------------------------------------------------
87 // rain_open (called by swmm_start in swmm5.c)
88 // rain_close (called by swmm_end in swmm5.c)
89
90 //-----------------------------------------------------------------------------
91 // Local functions
92 //-----------------------------------------------------------------------------
93 static void createRainFile(int count);
94 static int rainFileConflict(int i);
95 static void initRainFile(void);
96 static int findGageInFile(int i, int kount);
97 static int addGageToRainFile(int i);
98 static int findFileFormat(FILE *f, int i, int *hdrLines);
99 static int findNWSOnlineFormat(FILE *f, char *line);
100 static void readFile(FILE *f, int fileFormat, int hdrLines, DateTime day1,
101 DateTime day2);
102 static int readNWSLine(char *line, int fileFormat, DateTime day1,
103 DateTime day2);
104 static int readNwsOnlineValue(char* s, long* v, char* flag);
105 static int readCMCLine(char *line, int fileFormat, DateTime day1,
106 DateTime day2);
107 static int readStdLine(char *line, DateTime day1, DateTime day2);
108 static void saveAccumRainfall(DateTime date1, int hour, int minute, long v);
109 static void saveRainfall(DateTime date1, int hour, int minute, float x,
110 char isMissing);
111 static void setCondition(char flag);
112 static int getNWSInterval(char *elemType);
113 static int parseStdLine(char *line, int *year, int *month, int *day,
114 int *hour, int *minute, float *value);
115
116 //=============================================================================
117
118 58 void rain_open(void)
119 //
120 // Input: none
121 // Output: none
122 // Purpose: opens binary rain interface file and RDII processor.
123 //
124 {
125 int i;
126 int count;
127
128 // --- see how many gages get their data from a file
129 58 count = 0;
130
2/2
✓ Branch 0 taken 52 times.
✓ Branch 1 taken 58 times.
110 for (i = 0; i < Nobjects[GAGE]; i++)
131 {
132
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 39 times.
52 if ( Gage[i].dataSource == RAIN_FILE ) count++;
133 }
134 58 Frain.file = NULL;
135
2/2
✓ Branch 0 taken 47 times.
✓ Branch 1 taken 11 times.
58 if ( count == 0 )
136 {
137 47 Frain.mode = NO_FILE;
138 }
139
140 // --- see what kind of rain interface file to open
141
3/4
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
11 else switch ( Frain.mode )
142 {
143 9 case SCRATCH_FILE:
144 9 getTempFileName(Frain.name);
145
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
9 if ( (Frain.file = fopen(Frain.name, "w+b")) == NULL)
146 {
147 report_writeErrorMsg(ERR_RAIN_FILE_SCRATCH, "");
148 return;
149 }
150 9 break;
151
152 1 case USE_FILE:
153
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
1 if ( (Frain.file = fopen(Frain.name, "r+b")) == NULL)
154 {
155 report_writeErrorMsg(ERR_RAIN_FILE_OPEN, Frain.name);
156 return;
157 }
158 1 break;
159
160 1 case SAVE_FILE:
161
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
1 if ( (Frain.file = fopen(Frain.name, "w+b")) == NULL)
162 {
163 report_writeErrorMsg(ERR_RAIN_FILE_OPEN, Frain.name);
164 return;
165 }
166 1 break;
167 }
168
169 // --- create new rain file if required
170
4/4
✓ Branch 0 taken 49 times.
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 48 times.
58 if ( Frain.mode == SCRATCH_FILE || Frain.mode == SAVE_FILE )
171 {
172 10 createRainFile(count);
173 }
174
175 // --- initialize rain file
176
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 47 times.
58 if ( Frain.mode != NO_FILE ) initRainFile();
177
178 // --- open RDII processor (creates/opens a RDII interface file)
179 58 rdii_openRdii();
180 }
181
182 //=============================================================================
183
184 58 void rain_close(void)
185 //
186 // Input: none
187 // Output: none
188 // Purpose: closes rain interface file and RDII processor.
189 //
190 {
191
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 47 times.
58 if ( Frain.file )
192 {
193 11 fclose(Frain.file);
194
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 2 times.
11 if ( Frain.mode == SCRATCH_FILE ) remove(Frain.name);
195 }
196 58 Frain.file = NULL;
197 58 rdii_closeRdii();
198 58 }
199
200 //=============================================================================
201
202 10 void createRainFile(int count)
203 //
204 // Input: count = number of files to include in rain interface file
205 // Output: none
206 // Purpose: adds rain data from all rain gage files to the interface file.
207 //
208 {
209 int i, k;
210 10 int kount = count; // number of gages in data file
211 int filePos1; // starting byte of gage's header data
212 int filePos2; // starting byte of gage's rain data
213 int filePos3; // starting byte of next gage's data
214 int interval; // recording interval (sec)
215 10 int dummy = -1;
216 char staID[MAXMSG+1]; // gage's ID name
217 10 char fileStamp[] = "SWMM5-RAIN";
218
219 // --- make sure interface file is open and no error condition
220
2/4
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 10 times.
10 if ( ErrorCode || !Frain.file ) return;
221
222 // --- write file stamp & # gages to file
223 10 fwrite(fileStamp, sizeof(char), strlen(fileStamp), Frain.file);
224 10 fwrite(&kount, sizeof(int), 1, Frain.file);
225 10 filePos1 = ftell(Frain.file);
226
227 // --- write default fill-in header records to file for each gage
228 // (will be replaced later with actual records)
229
1/2
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
10 if ( count > 0 ) report_writeRainStats(-1, &RainStats);
230 10 strcpy(staID, " ");
231
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 10 times.
22 for ( i = 0; i < count; i++ )
232 {
233 12 fwrite(staID, sizeof(char), MAXMSG+1, Frain.file);
234
2/2
✓ Branch 0 taken 36 times.
✓ Branch 1 taken 12 times.
48 for ( k = 1; k <= 3; k++ )
235 36 fwrite(&dummy, sizeof(int), 1, Frain.file);
236 }
237 10 filePos2 = ftell(Frain.file);
238
239 // --- loop through project's rain gages,
240 // looking for ones using rain files
241
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 10 times.
22 for ( i = 0; i < Nobjects[GAGE]; i++ )
242 {
243
2/4
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 12 times.
12 if ( ErrorCode || Gage[i].dataSource != RAIN_FILE ) continue;
244
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
12 if ( rainFileConflict(i) ) break;
245
246 // --- position rain file to where data for gage will begin
247 12 fseek(Frain.file, filePos2, SEEK_SET);
248
249 // --- add gage's data to rain file
250
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 if ( addGageToRainFile(i) )
251 {
252 // --- write header records for gage to beginning of rain file
253 12 filePos3 = ftell(Frain.file);
254 12 fseek(Frain.file, filePos1, SEEK_SET);
255 12 sstrncpy(staID, Gage[i].staID, MAXMSG);
256 12 interval = Interval;
257 12 fwrite(staID, sizeof(char), MAXMSG+1, Frain.file);
258 12 fwrite(&interval, sizeof(int), 1, Frain.file);
259 12 fwrite(&filePos2, sizeof(int), 1, Frain.file);
260 12 fwrite(&filePos3, sizeof(int), 1, Frain.file);
261 12 filePos1 = ftell(Frain.file);
262 12 filePos2 = filePos3;
263 12 report_writeRainStats(i, &RainStats);
264 }
265 }
266
267 // --- if there was an error condition, then delete newly created file
268
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 if ( ErrorCode )
269 {
270 fclose(Frain.file);
271 Frain.file = NULL;
272 remove(Frain.name);
273 }
274 }
275
276 //=============================================================================
277
278 12 int rainFileConflict(int i)
279 //
280 // Input: i = rain gage index
281 // Output: returns 1 if file conflict found, 0 if not
282 // Purpose: checks if a rain gage's station ID matches another gage's
283 // station ID but the two use different rain data files.
284 //
285 {
286 int j;
287 12 char* staID = Gage[i].staID;
288 12 char* fname = Gage[i].fname;
289
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 12 times.
15 for (j = 0; j < i; j++)
290 {
291
1/4
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
3 if ( strcomp(Gage[j].staID, staID) && !strcomp(Gage[j].fname, fname) )
292 {
293 report_writeErrorMsg(ERR_RAIN_FILE_CONFLICT, Gage[i].ID);
294 return 1;
295 }
296 }
297 12 return 0;
298 }
299
300 //=============================================================================
301
302 12 int addGageToRainFile(int i)
303 //
304 // Input: i = rain gage index
305 // Output: returns 1 if successful, 0 if not
306 // Purpose: adds a gage's rainfall record to rain interface file
307 //
308 {
309 FILE* f; // pointer to rain file
310 int fileFormat; // file format code
311 int hdrLines; // number of header lines skipped
312
313 // --- let StationID point to NULL
314 12 StationID = NULL;
315
316 // --- check that rain file exists
317
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
12 if ( (f = fopen(Gage[i].fname, "rt")) == NULL )
318 report_writeErrorMsg(ERR_RAIN_FILE_DATA, Gage[i].fname);
319 else
320 {
321 12 fileFormat = findFileFormat(f, i, &hdrLines);
322
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 if ( fileFormat == UNKNOWN_FORMAT )
323 {
324 report_writeErrorMsg(ERR_RAIN_FILE_FORMAT, Gage[i].fname);
325 }
326 else
327 {
328 12 GageIndex = i;
329 12 readFile(f, fileFormat, hdrLines, Gage[i].startFileDate,
330 12 Gage[i].endFileDate);
331 }
332 12 fclose(f);
333 }
334
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 if ( ErrorCode ) return 0;
335 else
336 12 return 1;
337 }
338
339 //=============================================================================
340
341 11 void initRainFile(void)
342 //
343 // Input: none
344 // Output: none
345 // Purpose: initializes rain interface file for reading.
346 //
347 {
348 11 char fileStamp[] = "SWMM5-RAIN";
349 11 char fStamp[] = "SWMM5-RAIN";
350 int i;
351 int kount;
352 long filePos;
353
354 // --- make sure interface file is open and no error condition
355
2/4
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 11 times.
11 if ( ErrorCode || !Frain.file ) return;
356
357 // --- check that interface file contains proper file stamp
358 11 rewind(Frain.file);
359 11 fread(fStamp, sizeof(char), strlen(fileStamp), Frain.file);
360
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 if ( strcmp(fStamp, fileStamp) != 0 )
361 {
362 report_writeErrorMsg(ERR_RAIN_IFACE_FORMAT, "");
363 return;
364 }
365 11 fread(&kount, sizeof(int), 1, Frain.file);
366 11 filePos = ftell(Frain.file);
367
368 // --- locate information for each raingage in interface file
369
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 11 times.
24 for ( i = 0; i < Nobjects[GAGE]; i++ )
370 {
371
2/4
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 13 times.
13 if ( ErrorCode || Gage[i].dataSource != RAIN_FILE ) continue;
372
373 // --- match station ID for gage with one in file
374 13 fseek(Frain.file, filePos, SEEK_SET);
375
1/2
✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
13 if ( !findGageInFile(i, (int)kount) ||
376
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
13 Gage[i].startFilePos == Gage[i].endFilePos )
377 {
378 report_writeErrorMsg(ERR_RAIN_FILE_GAGE, Gage[i].ID);
379 }
380 }
381 }
382
383 //=============================================================================
384
385 13 int findGageInFile(int i, int kount)
386 //
387 // Input: i = rain gage index
388 // kount = number of rain gages stored on interface file
389 // Output: returns TRUE if successful, FALSE if not
390 // Purpose: checks if rain gage's station ID appears in interface file.
391 //
392 {
393 int k;
394 int interval;
395 int filePos1, filePos2;
396 13 char staID[MAXMSG+1] = "";
397
398
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 for ( k = 1; k <= kount; k++ )
399 {
400 16 fread(staID, sizeof(char), MAXMSG+1, Frain.file);
401 16 fread(&interval, sizeof(int), 1, Frain.file);
402 16 fread(&filePos1, sizeof(int), 1, Frain.file);
403 16 fread(&filePos2, sizeof(int), 1, Frain.file);
404
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 3 times.
16 if ( strcmp(staID, Gage[i].staID) == 0 )
405 {
406 // --- match found; save file parameters
407 13 Gage[i].rainType = RAINFALL_VOLUME;
408 13 Gage[i].rainInterval = interval;
409 13 Gage[i].startFilePos = (long)filePos1;
410 13 Gage[i].endFilePos = (long)filePos2;
411 13 Gage[i].currentFilePos = Gage[i].startFilePos;
412 13 return TRUE;
413 }
414 }
415 return FALSE;
416 }
417
418 //=============================================================================
419
420 12 int findFileFormat(FILE *f, int i, int *hdrLines)
421 //
422 // Input: f = ptr. to rain gage's rainfall data file
423 // i = rain gage index
424 // Output: hdrLines = number of header lines found in data file;
425 // returns type of format used in a rainfall data file
426 // Purpose: finds the format of a gage's rainfall data file.
427 //
428 {
429 int fileFormat;
430 int lineCount;
431 12 int maxCount = 5;
432 int n;
433 int div;
434 long sn2;
435 char recdType[4];
436 char elemType[5];
437 12 char coopID[6] = "";
438 char line[MAXLINE];
439 int year, month, day, hour, minute;
440 int elem;
441 float x;
442
443 // --- check first few lines for known formats
444 12 fileFormat = UNKNOWN_FORMAT;
445 12 hasStationName = FALSE;
446 12 UnitsFactor = 1.0;
447 12 Interval = 0;
448 12 *hdrLines = 0;
449
1/2
✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
24 for (lineCount = 1; lineCount <= maxCount; lineCount++)
450 {
451
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 24 times.
24 if ( fgets(line, MAXLINE, f) == NULL ) return fileFormat;
452
453 // --- check for NWS space delimited format
454 24 n = sscanf(line, "%6ld %2d %4s", &sn2, &div, elemType);
455
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 21 times.
24 if ( n == 3 )
456 {
457 3 Interval = getNWSInterval(elemType);
458 3 TimeOffset = Interval;
459
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 time.
3 if ( Interval > 0 )
460 {
461 2 fileFormat = NWS_SPACE_DELIMITED;
462 2 break;
463 }
464 }
465
466 // --- check for NWS space delimited format w/ station name
467 22 n = sscanf(&line[37], "%2d %4s %2s %4d", &div, elemType, recdType, &year);
468
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 19 times.
22 if ( n == 4 )
469 {
470 3 Interval = getNWSInterval(elemType);
471 3 TimeOffset = Interval;
472
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if ( Interval > 0 )
473 {
474 fileFormat = NWS_SPACE_DELIMITED;
475 hasStationName = TRUE;
476 break;
477 }
478 }
479
480 // --- check for NWS coma delimited format
481 22 n = sscanf(line, "%6ld,%2d,%4s", &sn2, &div, elemType);
482
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 21 times.
22 if ( n == 3 )
483 {
484 1 Interval = getNWSInterval(elemType);
485 1 TimeOffset = Interval;
486
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1 if ( Interval > 0 )
487 {
488 1 fileFormat = NWS_COMMA_DELIMITED;
489 1 break;
490 }
491 }
492
493 // --- check for NWS comma delimited format w/ station name
494 21 n = sscanf(&line[37], "%2d,%4s,%2s,%4d", &div, elemType, recdType, &year);
495
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 21 times.
21 if ( n == 4 )
496 {
497 Interval = getNWSInterval(elemType);
498 TimeOffset = Interval;
499 if ( Interval > 0 )
500 {
501 fileFormat = NWS_COMMA_DELIMITED;
502 hasStationName = TRUE;
503 break;
504 }
505 }
506
507 // --- check for NWS TAPE format
508 21 n = sscanf(line, "%3s%6ld%2d%4s", recdType, &sn2, &div, elemType);
509
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 13 times.
21 if ( n == 4 )
510 {
511 8 Interval = getNWSInterval(elemType);
512 8 TimeOffset = Interval;
513
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 7 times.
8 if ( Interval > 0 )
514 {
515 1 fileFormat = NWS_TAPE;
516 1 break;
517 }
518 }
519
520 // --- check for NWS Online Retrieval format
521 20 n = sscanf(line, "%5s%6ld", coopID, &sn2);
522
4/4
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 7 times.
20 if ( n == 2 && strcmp(coopID, "COOP:") == 0 )
523 {
524 1 fileFormat = findNWSOnlineFormat(f, line);
525 1 break;
526 }
527
528 // --- check for AES type
529 19 n = sscanf(line, "%7ld%3d%2d%2d%3d", &sn2, &year, &month, &day, &elem);
530
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 18 times.
19 if ( n == 5 )
531 {
532
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1 if ( elem == 123 && strlen(line) >= 185 )
533 {
534 fileFormat = AES_HLY;
535 Interval = 3600;
536 TimeOffset = Interval;
537 UnitsFactor = 1.0/MMperINCH;
538 break;
539 }
540 }
541
542 // --- check for CMC types
543 19 n = sscanf(line, "%7ld%4d%2d%2d%3d", &sn2, &year, &month, &day, &elem);
544
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 18 times.
19 if ( n == 5 )
545 {
546
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1 if ( elem == 159 && strlen(line) >= 691 )
547 {
548 fileFormat = CMC_FIF;
549 Interval = 900;
550 }
551
2/4
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
1 else if ( elem == 123 && strlen(line) >= 186 )
552 {
553 1 fileFormat = CMC_HLY;
554 1 Interval = 3600;
555 }
556
2/4
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
1 if ( fileFormat == CMC_FIF || fileFormat == CMC_HLY )
557 {
558 1 TimeOffset = Interval;
559 1 UnitsFactor = 1.0/MMperINCH;
560 1 break;
561 }
562 }
563
564 // --- check for standard format
565
2/2
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 12 times.
18 if ( parseStdLine(line, &year, &month, &day, &hour, &minute, &x) )
566 {
567 6 fileFormat = STD_SPACE_DELIMITED;
568 6 RainType = Gage[i].rainType;
569 6 Interval = Gage[i].rainInterval;
570
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if ( Gage[i].rainUnits == SI ) UnitsFactor = 1.0/MMperINCH;
571 6 TimeOffset = 0;
572 6 StationID = Gage[i].staID;
573 6 break;
574 }
575 12 (*hdrLines)++;
576
577 }
578
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 if ( fileFormat != UNKNOWN_FORMAT ) Gage[i].rainInterval = Interval;
579 12 return fileFormat;
580 }
581
582 //=============================================================================
583
584 1 int findNWSOnlineFormat(FILE *f, char *line)
585 //
586 // Input: f = pointer to rainfall data file
587 // line = line read from rainfall data file
588 // Output:
589 // Purpose: determines the file format for an NWS Online Retrieval data file.
590 //
591 {
592 int n;
593 1 int fileFormat = UNKNOWN_FORMAT;
594 char* str;
595
596 // --- read in the first header line of the file
597 1 rewind(f);
598 1 fgets(line, MAXLINE, f);
599
600 // --- if 'HPCP' appears then file is for hourly data
601
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1 if ( (str = strstr(line, "HPCP")) != NULL )
602 {
603 1 Interval = 3600;
604 1 TimeOffset = Interval;
605 1 ValueOffset = (int)(str - line);
606 1 fileFormat = NWS_ONLINE_60;
607 }
608
609 // --- if 'QPCP" appears then file is for 15 minute data
610 else if ( (str = strstr(line, "QPCP")) != NULL )
611 {
612 Interval = 900;
613 TimeOffset = Interval;
614 ValueOffset = (int)(str - line);
615 fileFormat = NWS_ONLINE_15;
616 }
617 else return UNKNOWN_FORMAT;
618
619 // --- find position in line where rainfall date begins
620 // (11 characters before last occurrence of ':')
621 // --- read in first line of data
622
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 for (n = 1; n <= 5; n++)
623 {
624
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if ( fgets(line, MAXLINE, f) == NULL ) return UNKNOWN_FORMAT;
625
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
2 if ( strstr(line, "COOP:") == NULL ) continue;
626
627 // --- find pointer to last occurrence of time separator character (':')
628 1 str = strrchr(line, ':');
629
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1 if ( str == NULL ) return UNKNOWN_FORMAT;
630
631 // --- use pointer arithmetic to convert pointer to character position
632 1 n = (int)(str - line);
633 1 DataOffset = n - 11;
634 1 return fileFormat;
635 }
636 return UNKNOWN_FORMAT;
637 }
638
639 //=============================================================================
640
641 15 int getNWSInterval(char *elemType)
642 //
643 // Input: elemType = code from NWS rainfall file
644 // Output: returns rainfall recording interval (sec)
645 // Purpose: decodes NWS rain gage recording interval value
646 //
647 {
648
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 11 times.
15 if ( strcmp(elemType, "HPCP") == 0 ) return 3600; // 1 hr rainfall
649
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 else if ( strcmp(elemType, "QPCP") == 0 ) return 900; // 15 min rainfall
650
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 else if ( strcmp(elemType, "QGAG") == 0 ) return 900; // 15 min rainfall
651 11 else return 0;
652 }
653
654 //=============================================================================
655
656 12 void readFile(FILE *f, int fileFormat, int hdrLines, DateTime day1,
657 DateTime day2)
658 //
659 // Input: f = ptr. to gage's rainfall data file
660 // fileFormat = code of data file's format
661 // hdrLines = number of header lines in data file
662 // day1 = starting day of record of interest
663 // day2 = ending day of record of interest
664 // Output: none
665 // Purpose: reads rainfall records from gage's data file to interface file.
666 //
667 {
668 char line[MAXLINE];
669 int i, n;
670
671 12 rewind(f);
672 12 RainStats.startDate = NO_DATE;
673 12 RainStats.endDate = NO_DATE;
674 12 RainStats.periodsRain = 0;
675 12 RainStats.periodsMissing = 0;
676 12 RainStats.periodsMalfunc = 0;
677 12 RainAccum = 0.0;
678 12 AccumStartDate = NO_DATE;
679 12 PreviousDate = NO_DATE;
680
681
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 12 times.
24 for (i = 1; i <= hdrLines; i++)
682 {
683
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
12 if ( fgets(line, MAXLINE, f) == NULL ) return;
684 }
685
2/2
✓ Branch 1 taken 80337 times.
✓ Branch 2 taken 12 times.
80349 while ( fgets(line, MAXLINE, f) != NULL )
686 {
687
3/4
✓ Branch 0 taken 79788 times.
✓ Branch 1 taken 548 times.
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
80337 switch (fileFormat)
688 {
689 79788 case STD_SPACE_DELIMITED:
690 79788 n = readStdLine(line, day1, day2);
691 79788 break;
692
693 548 case NWS_TAPE:
694 case NWS_SPACE_DELIMITED:
695 case NWS_COMMA_DELIMITED:
696 case NWS_ONLINE_60:
697 case NWS_ONLINE_15:
698 548 n = readNWSLine(line, fileFormat, day1, day2);
699 548 break;
700
701 1 case AES_HLY:
702 case CMC_FIF:
703 case CMC_HLY:
704 1 n = readCMCLine(line, fileFormat, day1, day2);
705 1 break;
706
707 default:
708 n = -1;
709 break;
710 }
711
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 80337 times.
80337 if ( n < 0 ) break;
712 }
713 }
714
715 //=============================================================================
716
717 548 int readNWSLine(char *line, int fileFormat, DateTime day1, DateTime day2)
718 //
719 // Input: line = line of data from rainfall data file
720 // fileFormat = code of data file's format
721 // day1 = starting day of record of interest
722 // day2 = ending day of record of interest
723 // Output: returns -1 if past end of desired record, 0 if data line could
724 // not be read successfully or 1 if line read successfully
725 // Purpose: reads a line of data from a rainfall data file and writes its
726 // data to the rain interface file.
727 //
728 {
729 char flag1, flag2, isMissing;
730 DateTime date1;
731 548 long result = 1;
732 int k, y, m, d, n;
733 int hour, minute;
734 long v;
735 float x;
736 548 int lineLength = (int)strlen(line)-1;
737 548 int nameLength = 0;
738
739 // --- get year, month, & day from line
740
4/5
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 522 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 24 times.
✗ Branch 4 not taken.
548 switch ( fileFormat )
741 {
742 1 case NWS_TAPE:
743
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1 if ( lineLength <= 30 ) return 0;
744
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1 if (sscanf(&line[17], "%4d%2d%4d%3d", &y, &m, &d, &n) < 4) return 0;
745 1 k = 30;
746 1 break;
747
748 522 case NWS_SPACE_DELIMITED:
749
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 522 times.
522 if ( hasStationName ) nameLength = 31;
750
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 522 times.
522 if ( lineLength <= 28 + nameLength ) return 0;
751 522 k = 18 + nameLength;
752
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 522 times.
522 if (sscanf(&line[k], "%4d %2d %2d", &y, &m, &d) < 3) return 0;
753 522 k = k + 10;
754 522 break;
755
756 1 case NWS_COMMA_DELIMITED:
757
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1 if ( lineLength <= 28 ) return 0;
758
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1 if ( sscanf(&line[18], "%4d,%2d,%2d", &y, &m, &d) < 3 ) return 0;
759 1 k = 28;
760 1 break;
761
762 24 case NWS_ONLINE_60:
763 case NWS_ONLINE_15:
764
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
24 if ( lineLength <= DataOffset + 23 ) return 0;
765
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
24 if ( sscanf(&line[DataOffset], "%4d%2d%2d", &y, &m, &d) < 3 ) return 0;
766 24 k = DataOffset + 8;
767 24 break;
768
769 default: return 0;
770 }
771
772 // --- see if date is within period of record requested
773 548 date1 = datetime_encodeDate(y, m, d);
774
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 548 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
548 if ( day1 != NO_DATE && date1 < day1 ) return 0;
775
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 548 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
548 if ( day2 != NO_DATE && date1 > day2 ) return -1;
776
777 // --- read each recorded rainfall time, value, & codes from line
778
2/2
✓ Branch 0 taken 13080 times.
✓ Branch 1 taken 26 times.
13106 while ( k < lineLength )
779 {
780 13080 flag1 = 0;
781 13080 flag2 = 0;
782 13080 v = 99999;
783 13080 hour = 25;
784 13080 minute = 0;
785
4/5
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 13050 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 24 times.
✗ Branch 4 not taken.
13080 switch ( fileFormat )
786 {
787 3 case NWS_TAPE:
788 3 n = sscanf(&line[k], "%2d%2d%6ld%c%c",
789 &hour, &minute, &v, &flag1, &flag2);
790 3 k += 12;
791 3 break;
792
793 13050 case NWS_SPACE_DELIMITED:
794 13050 n = sscanf(&line[k], " %2d%2d %6ld %c %c",
795 &hour, &minute, &v, &flag1, &flag2);
796 13050 k += 16;
797 13050 break;
798
799 3 case NWS_COMMA_DELIMITED:
800 3 n = sscanf(&line[k], ",%2d%2d,%6ld,%c,%c",
801 &hour, &minute, &v, &flag1, &flag2);
802 3 k += 16;
803 3 break;
804
805 24 case NWS_ONLINE_60:
806 case NWS_ONLINE_15:
807 24 n = sscanf(&line[k], " %2d:%2d", &hour, &minute);
808 24 n += readNwsOnlineValue(&line[ValueOffset], &v, &flag1);
809
810 // --- ending hour 0 is really hour 24 of previous day
811
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 23 times.
24 if ( hour == 0 )
812 {
813 1 hour = 24;
814 1 date1 -= 1.0;
815 }
816 24 k += lineLength;
817 24 break;
818
819 default: n = 0;
820 }
821
822 // --- check that we at least have an hour, minute & value
823 // (codes might be left off of the line)
824
3/4
✓ Branch 0 taken 13080 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 12558 times.
✓ Branch 3 taken 522 times.
13080 if ( n < 3 || hour >= 25 ) break;
825
826 // --- set special condition code & update daily & hourly counts
827
828 12558 setCondition(flag1);
829
1/2
✓ Branch 0 taken 12558 times.
✗ Branch 1 not taken.
12558 if ( Condition == DELETED_PERIOD ||
830
1/2
✓ Branch 0 taken 12558 times.
✗ Branch 1 not taken.
12558 Condition == MISSING_PERIOD ||
831
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12558 times.
12558 flag1 == 'M' ) isMissing = TRUE;
832
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12558 times.
12558 else if ( v >= 9999 ) isMissing = TRUE;
833 12558 else isMissing = FALSE;
834
835 // --- handle accumulation codes
836
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 12557 times.
12558 if ( flag1 == 'a' )
837 {
838 1 AccumStartDate = date1 + datetime_encodeTime(hour, minute, 0);
839 }
840
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 12556 times.
12557 else if ( flag1 == 'A' )
841 {
842 1 saveAccumRainfall(date1, hour, minute, v);
843 }
844
845 // --- handle all other conditions
846 else
847 {
848 // --- convert rain measurement to inches & save it
849 12556 x = (float)v / 100.0f;
850
3/4
✓ Branch 0 taken 10269 times.
✓ Branch 1 taken 2287 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 10269 times.
12556 if ( x > 0 || isMissing )
851 2287 saveRainfall(date1, hour, minute, x, isMissing);
852 }
853
854 // --- reset condition code if special condition period ended
855
4/6
✓ Branch 0 taken 12557 times.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 12557 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 12557 times.
12558 if ( flag1 == 'A' || flag1 == '}' || flag1 == ']') Condition = 0;
856 }
857 548 return result;
858 }
859
860 //=============================================================================
861
862 24 int readNwsOnlineValue(char* s, long* v, char* flag)
863 //
864 // Input: s = portion of rainfall record in NWS online format
865 // Output: v = rainfall amount in hundreths of an inch
866 // flag = special condition flag
867 // returns number of items read from s.
868 // Purpose: reads rainfall value and condition flag from a NWS online
869 // rainfall record.
870 //
871 {
872 int n;
873 24 float x = 99.99f;
874
875 // --- check for newer format of decimal inches
876
1/2
✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
24 if ( strchr(s, '.') )
877 {
878 24 n = sscanf(s, "%f %c", &x, flag);
879
880 // --- convert to integer hundreths of an inch
881 24 *v = (long)(100.0f * x + 0.5f);
882 }
883
884 // --- older format of hundreths of an inch
885 else n = sscanf(s, "%ld %c", v, flag);
886 24 return n;
887 }
888
889 //=============================================================================
890
891 12558 void setCondition(char flag)
892 {
893
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 12556 times.
12558 switch ( flag )
894 {
895 2 case 'a':
896 case 'A':
897 2 Condition = ACCUMULATED_PERIOD;
898 2 break;
899 case '{':
900 case '}':
901 Condition = DELETED_PERIOD;
902 break;
903 case '[':
904 case ']':
905 Condition = MISSING_PERIOD;
906 break;
907 12556 default:
908 12556 Condition = NO_CONDITION;
909 }
910 12558 }
911
912 //=============================================================================
913
914 1 int readCMCLine(char *line, int fileFormat, DateTime day1, DateTime day2)
915 //
916 // Input: line = line of data from rainfall data file
917 // fileFormat = code of data file's format
918 // day1 = starting day of record of interest
919 // day2 = ending day of record of interest
920 // Output: returns -1 if past end of desired record, 0 if data line could
921 // not be read successfully or 1 if line read successfully
922 // Purpose: reads a line of data from an AES or CMC rainfall data file and
923 // writes its data to the rain interface file.
924 //
925 {
926 char flag, isMissing;
927 DateTime date1;
928 long sn, v;
929 int col, j, jMax, elem, y, m, d, hour, minute;
930 float x;
931
932 // --- get year, month, day & element code from line
933
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1 if ( fileFormat == AES_HLY )
934 {
935 if ( sscanf(line, "%7ld%3d%2d%2d%3d", &sn, &y, &m, &d, &elem) < 5 )
936 return 0;
937 if ( y < 100 ) y = y + 2000;
938 else y = y + 1000;
939 col = 17;
940 }
941 else
942 {
943
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1 if ( sscanf(line, "%7ld%4d%2d%2d%3d", &sn, &y, &m, &d, &elem) < 5 )
944 return 0;
945 1 col = 18;
946 }
947
948 // --- see if date is within period of record requested
949 1 date1 = datetime_encodeDate(y, m, d);
950
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1 if ( day1 != NO_DATE && date1 < day1 ) return 0;
951
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1 if ( day2 != NO_DATE && date1 > day2 ) return -1;
952
953 // --- make sure element code is for rainfall
954
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1 if ( fileFormat == AES_HLY && elem != 123 ) return 0;
955
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1 else if ( fileFormat == CMC_FIF && elem != 159 ) return 0;
956
2/4
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 time.
1 else if ( fileFormat == CMC_HLY && elem != 123 ) return 0;
957
958 // --- read rainfall from each recording interval
959 1 hour = 0; // starting hour
960 1 minute = 0; // starting minute
961 1 jMax = 24; // # recording intervals
962
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1 if ( fileFormat == CMC_FIF ) jMax = 96;
963
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 1 time.
25 for (j=1; j<=jMax; j++)
964 {
965
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
24 if ( sscanf(&line[col], "%6ld%c", &v, &flag) < 2 ) return 0;
966 24 col += 7;
967
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
24 if ( v == -99999 ) isMissing = TRUE;
968 24 else isMissing = FALSE;
969
970 // --- convert rain measurement from 0.1 mm to inches and save it
971 24 x = (float)( (double)v / 10.0 / MMperINCH);
972
3/4
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 21 times.
24 if ( x > 0 || isMissing)
973 {
974 3 saveRainfall(date1, hour, minute, x, isMissing);
975 }
976
977 // --- update hour & minute for next interval
978
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
24 if ( fileFormat == CMC_FIF )
979 {
980 minute += 15;
981 if ( minute == 60 )
982 {
983 minute = 0;
984 hour++;
985 }
986 }
987 24 else hour++;
988 }
989 1 return 1;
990 }
991
992 //=============================================================================
993
994 79788 int readStdLine(char *line, DateTime day1, DateTime day2)
995 //
996 // Input: line = line of data from a standard rainfall data file
997 // day1 = starting day of record of interest
998 // day2 = ending day of record of interest
999 // Output: returns -1 if past end of desired record, 0 if data line could
1000 // not be read successfully or 1 if line read successfully
1001 // Purpose: reads a line of data from a standard rainfall data file and
1002 // writes its data to the rain interface file.
1003 //
1004 {
1005 DateTime date1;
1006 DateTime date2;
1007 int year, month, day, hour, minute;
1008 float x;
1009
1010 // --- parse data from input line
1011
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 79788 times.
79788 if (!parseStdLine(line, &year, &month, &day, &hour, &minute, &x)) return 0;
1012
1013 // --- see if date is within period of record requested
1014 79788 date1 = datetime_encodeDate(year, month, day);
1015
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 79788 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
79788 if ( day1 != NO_DATE && date1 < day1 ) return 0;
1016
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 79788 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
79788 if ( day2 != NO_DATE && date1 > day2 ) return -1;
1017
1018 // --- see if record is out of sequence
1019 79788 date2 = date1 + datetime_encodeTime(hour, minute, 0);
1020
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 79788 times.
79788 if ( date2 <= PreviousDate )
1021 {
1022 report_writeErrorMsg(ERR_RAIN_FILE_SEQUENCE, Gage[GageIndex].fname);
1023 report_writeLine(line);
1024 return -1;
1025 }
1026 79788 PreviousDate = date2;
1027
1028
1/3
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 79788 times.
79788 switch (RainType)
1029 {
1030 case RAINFALL_INTENSITY:
1031 x = x * Interval / 3600.0f;
1032 break;
1033
1034 case CUMULATIVE_RAINFALL:
1035 if ( x >= RainAccum )
1036 {
1037 x = x - RainAccum;
1038 RainAccum += x;
1039 }
1040 else RainAccum = x;
1041 break;
1042 }
1043 79788 x *= (float)UnitsFactor;
1044
1045 // --- save rainfall to binary interface file
1046 79788 saveRainfall(date1, hour, minute, x, FALSE);
1047 79788 return 1;
1048 }
1049
1050 //=============================================================================
1051
1052 79806 int parseStdLine(char *line, int *year, int *month, int *day, int *hour,
1053 int *minute, float *value)
1054 //
1055 // Input: line = line of data from a standard rainfall data file
1056 // Output: *year = year when rainfall occurs
1057 // *month = month of year when rainfall occurs
1058 // *day = day of month when rainfall occurs
1059 // *hour = hour of day when rainfall occurs
1060 // *minute = minute of hour when rainfall occurs
1061 // *value = rainfall value (user units);
1062 // returns 0 if data line could not be parsed successfully or
1063 // 1 if line parsed successfully
1064 // Purpose: parses a line of data from a standard rainfall data file.
1065 //
1066 {
1067 int n;
1068 char token[MAXLINE];
1069
1070 79806 n = sscanf(line, "%s %d %d %d %d %d %f", token, year, month, day, hour, minute, value);
1071
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 79794 times.
79806 if ( n < 7 ) return 0;
1072
3/4
✓ Branch 0 taken 79788 times.
✓ Branch 1 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 79788 times.
79794 if ( StationID != NULL && !strcomp(token, StationID) ) return 0;
1073 79794 return 1;
1074 }
1075
1076 //=============================================================================
1077
1078 1 void saveAccumRainfall(DateTime date1, int hour, int minute, long v)
1079 //
1080 // Input: date1 = date of latest rainfall reading (in DateTime format)
1081 // hour = hour of day of latest rain reading
1082 // minute = minute of hour of latest rain reading
1083 // v = accumulated rainfall reading in hundreths of inches
1084 // Output: none
1085 // Purpose: divides accumulated rainfall evenly into individual recording
1086 // periods over the accumulation period and writes each period's
1087 // rainfall to the binary rainfall file.
1088 //
1089 {
1090 DateTime date2;
1091 int n, j;
1092 float x;
1093
1094 // --- return if accumulated start date is missing
1095
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1 if ( AccumStartDate == NO_DATE ) return;
1096
1097 // --- find number of recording intervals over accumulation period
1098 1 date2 = date1 + datetime_encodeTime(hour, minute, 0);
1099 1 n = (datetime_timeDiff(date2, AccumStartDate) / Interval) + 1;
1100
1101 // --- update count of rain or missing periods
1102
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1 if ( v == 99999 )
1103 {
1104 RainStats.periodsMissing += n;
1105 return;
1106 }
1107 1 RainStats.periodsRain += n;
1108
1109 // --- divide accumulated amount evenly into each period
1110 1 x = (float)v / (float)n / 100.0f;
1111
1112 // --- save this amount to file for each period
1113
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1 if ( x > 0.0f )
1114 {
1115 1 date2 = datetime_addSeconds(AccumStartDate, -TimeOffset);
1116
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1 if ( RainStats.startDate == NO_DATE ) RainStats.startDate = date2;
1117
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 time.
5 for (j = 0; j < n; j++)
1118 {
1119 4 fwrite(&date2, sizeof(DateTime), 1, Frain.file);
1120 4 fwrite(&x, sizeof(float), 1, Frain.file);
1121 4 date2 = datetime_addSeconds(date2, Interval);
1122 4 RainStats.endDate = date2;
1123 }
1124 }
1125
1126 // --- reset start of accumulation period
1127 1 AccumStartDate = NO_DATE;
1128 }
1129
1130
1131 //=============================================================================
1132
1133 82078 void saveRainfall(DateTime date1, int hour, int minute, float x, char isMissing)
1134 //
1135 // Input: date1 = date of rainfall reading (in DateTime format)
1136 // hour = hour of day of current rain reading
1137 // minute = minute of hour of current rain reading
1138 // x = rainfall reading in inches
1139 // isMissing = TRUE if rainfall value is missing
1140 // Output: none
1141 // Purpose: writes current rainfall reading from an external rainfall file
1142 // to project's binary rainfall file.
1143 //
1144 {
1145 DateTime date2;
1146 int seconds;
1147
1148
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 82078 times.
82078 if ( isMissing ) RainStats.periodsMissing++;
1149 82078 else RainStats.periodsRain++;
1150
1151 // --- if rainfall not missing then save it to rainfall interface file
1152
1/2
✓ Branch 0 taken 82078 times.
✗ Branch 1 not taken.
82078 if ( !isMissing )
1153 {
1154 82078 seconds = 3600*hour + 60*minute - TimeOffset;
1155 82078 date2 = datetime_addSeconds(date1, seconds);
1156
1157 // --- write date & value (in inches) to interface file
1158 82078 fwrite(&date2, sizeof(DateTime), 1, Frain.file);
1159 82078 fwrite(&x, sizeof(float), 1, Frain.file);
1160
1161 // --- update actual start & end of record dates
1162
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 82067 times.
82078 if ( RainStats.startDate == NO_DATE ) RainStats.startDate = date2;
1163 82078 RainStats.endDate = date2;
1164 }
1165 82078 }
1166 //=============================================================================
1167