OpenCPN Partial API docs
Loading...
Searching...
No Matches
TCDS_Ascii_Harmonic.cpp
1/***************************************************************************
2 *
3 * Project: OpenCPN
4 *
5 ***************************************************************************
6 * Copyright (C) 2013 by David S. Register *
7 * *
8 * This program is free software; you can redistribute it and/or modify *
9 * it under the terms of the GNU General Public License as published by *
10 * the Free Software Foundation; either version 2 of the License, or *
11 * (at your option) any later version. *
12 * *
13 * This program is distributed in the hope that it will be useful, *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16 * GNU General Public License for more details. *
17 * *
18 * You should have received a copy of the GNU General Public License *
19 * along with this program; if not, write to the *
20 * Free Software Foundation, Inc., *
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
22 **************************************************************************/
23
24#include <wx/filename.h>
25#include <wx/tokenzr.h>
26
27#include <math.h>
28
29#include "TCDS_Ascii_Harmonic.h"
30
31#ifndef M_PI
32#define M_PI ((2) * (acos(0.0)))
33#endif
34
35#define IFF_OPEN 0
36#define IFF_CLOSE 1
37#define IFF_SEEK 2
38#define IFF_TELL 3
39#define IFF_READ 4
40
41typedef struct {
42 void *next;
43 short int rec_start;
44 char *name;
46
47/* Turn a time displacement of the form [-]HH:MM into the number of seconds. */
48static int hhmm2seconds(char *hhmm) {
49 int h, m;
50 char s;
51 if (sscanf(hhmm, "%d:%d", &h, &m) != 2) return (0);
52 if (sscanf(hhmm, "%c", &s) != 1) return (0);
53 if (h < 0 || s == '-') m = -m;
54 return h * 3600 + m * 60;
55}
56
57TCDS_Ascii_Harmonic::TCDS_Ascii_Harmonic() {
58 // Initialize member variables
59 m_IndexFile = NULL;
60
61 m_cst_speeds = NULL;
62 m_cst_nodes = NULL;
63 m_cst_epochs = NULL;
64 m_work_buffer = NULL;
65
66 num_IDX = 0;
67 num_nodes = 0;
68 num_csts = 0;
69 num_epochs = 0;
70}
71
72TCDS_Ascii_Harmonic::~TCDS_Ascii_Harmonic() {
73 free_data();
74
75 m_msd_array.Clear();
76}
77
78TC_Error_Code TCDS_Ascii_Harmonic::LoadData(const wxString &data_file_path) {
79 if (m_IndexFile) IndexFileIO(IFF_CLOSE, 0);
80
81 m_indexfile_name = data_file_path;
82
83 TC_Error_Code error_return = init_index_file();
84 if (error_return != TC_NO_ERROR) return error_return;
85
86 wxFileName f(data_file_path);
87 m_harmfile_name = f.GetPath(wxPATH_GET_SEPARATOR | wxPATH_GET_VOLUME);
88 m_harmfile_name += f.GetName();
89 error_return = LoadHarmonicConstants(m_harmfile_name);
90
91 // Mark the index entries individually with invariant harmonic constants
92 unsigned int max_index = GetMaxIndex();
93 for (unsigned int i = 0; i < max_index; i++) {
94 IDX_entry *pIDX = GetIndexEntry(i);
95 if (pIDX) {
96 pIDX->num_nodes = num_nodes;
97 pIDX->num_csts = num_csts;
98 pIDX->num_epochs = num_epochs;
99 pIDX->m_cst_speeds = m_cst_speeds;
100 pIDX->m_cst_nodes = m_cst_nodes;
101 pIDX->m_cst_epochs = m_cst_epochs;
102 pIDX->first_year = m_first_year;
103 pIDX->m_work_buffer = m_work_buffer;
104 }
105 }
106
107 return error_return;
108}
109
110IDX_entry *TCDS_Ascii_Harmonic::GetIndexEntry(int n_index) {
111 return &m_IDX_array[n_index];
112}
113
114TC_Error_Code TCDS_Ascii_Harmonic::init_index_file() {
115 long int xref_start = 0;
116
117 num_IDX = 0;
118
119 m_abbreviation_array.clear();
120 m_IDX_array.Clear();
121 // free_harmonic_file_list();
122 int have_index = 0;
123 int index_in_memory = 0;
124
125 if (IndexFileIO(IFF_OPEN, 0)) {
126 while (IndexFileIO(IFF_READ, 0)) {
127 if ((index_line_buffer[0] == '#') || (index_line_buffer[0] <= ' '))
128 ; // Skip comment lines
129 else if (!have_index && !xref_start) {
130 if (!strncmp(index_line_buffer, "XREF", 4))
131 xref_start = IndexFileIO(IFF_TELL, 0);
132 } else if (!have_index && !strncmp(index_line_buffer, "*END*", 5)) {
133 if (m_abbreviation_array.empty()) {
134 IndexFileIO(IFF_CLOSE, 0);
135 return (TC_INDEX_FILE_CORRUPT); // missing at least some data so no
136 // valid index
137 }
138 // We're done with abbreviation list (and no errors)
139 else
140 have_index = 1;
141 } // found *END* of cross reference
142
143 else if (!have_index && xref_start) {
144 wxString line(index_line_buffer, wxConvUTF8);
145
146 abbr_entry entry;
147
148 wxStringTokenizer tkz(line, _T(" "));
149 wxString token = tkz.GetNextToken();
150 if (token.IsSameAs(_T("REGION"), FALSE))
151 entry.type = REGION;
152 else if (token.IsSameAs(_T("COUNTRY"), FALSE))
153 entry.type = COUNTRY;
154 else if (token.IsSameAs(_T("STATE"), FALSE))
155 entry.type = STATE;
156
157 token = tkz.GetNextToken();
158 entry.short_s = token;
159
160 entry.long_s = line.Mid(tkz.GetPosition()).Strip();
161
162 m_abbreviation_array.push_back(entry);
163
164 }
165
166 else if (have_index && (strchr("TtCcIUu", index_line_buffer[0]))) {
167 // Load index file data .
168 num_IDX++; // Keep counting entries for harmonic file stuff
169 IDX_entry *pIDX = new IDX_entry;
170 pIDX->source_data_type = SOURCE_TYPE_ASCII_HARMONIC;
171 pIDX->pDataSource = NULL;
172
173 index_in_memory = TRUE;
174 pIDX->Valid15 = 0;
175
176 if (TC_NO_ERROR != build_IDX_entry(pIDX)) {
177 }
178
179 m_IDX_array.Add(pIDX);
180 }
181
182#if 0
183 else if (have_index && (index_line_buffer[0] == 'H')) {
184 // This is a new harmonic file name.
185 sscanf(index_line, "Harmonic %s", s1);
186 pHarmonic = harmonic_file_list;
187 while (pHarmonic && pHarmonic->next)
188 pHarmonic = (harmonic_file_entry *)pHarmonic->next;
189 pHarmonic_prev = pHarmonic;
190 pHarmonic = (harmonic_file_entry *)malloc(sizeof(harmonic_file_entry));
191 if (NULL == pHarmonic) {
192 // no_mem_msg();
193 free_harmonic_file_list();
194 }
195 else {
196 if (!harmonic_file_list)
197 harmonic_file_list = pHarmonic;
198 else pHarmonic_prev->next = pHarmonic;
199 pHarmonic->next = NULL;
200 pHarmonic->rec_start = num_IDX;
201 if (allocate_copy_string(&pHarmonic->name,s1)) {
202 // no_mem_msg();
203 free_harmonic_file_list();
204 }
205 }
206 }
207#endif
208 } // while (more file)
209 if (index_in_memory) IndexFileIO(IFF_CLOSE, 0); // All done with file
210 } // index file can't be opened
211 // if (hwndBusy) DestroyWindow(hwndBusy);
212
213 // max_IDX = num_IDX;
214 return (TC_NO_ERROR);
215}
216
217// ----------------------------------
218// Decode an index data line into an IDX_entry
219// ----------------------------------
220
221TC_Error_Code TCDS_Ascii_Harmonic::build_IDX_entry(IDX_entry *pIDX) {
222 int TZHr, TZMin;
223 char stz[80];
224
225 pIDX->pref_sta_data = NULL; // no reference data yet
226 pIDX->IDX_Useable = 1; // but assume data is OK
227
228 pIDX->IDX_tzname = NULL;
229 stz[0] = 0;
230
231 if (7 != sscanf(index_line_buffer, "%c%s%lf%lf%d:%d%*c%[^\r\n]",
232 &pIDX->IDX_type, &pIDX->IDX_zone[0], &pIDX->IDX_lon,
233 &pIDX->IDX_lat, &TZHr, &TZMin, &pIDX->IDX_station_name[0]))
234 return (TC_INDEX_ENTRY_BAD);
235
236 if (TZHr < 0 && TZMin > 0)
237 TZMin = -TZMin; // correct for negative timezones with fractional hours
238 // (NewFoundland)
239 pIDX->IDX_time_zone = TZHr * 60 + TZMin;
240
241 if (strchr("tcUu",
242 index_line_buffer[0])) { // Substation so get second line of info
243 IndexFileIO(IFF_READ, 0);
244
245 if (index_line_buffer[0] == '^') // Opencpn special
246 {
247 if (11 != sscanf(index_line_buffer,
248 "%*c%d %f %f %d %f %f %d %d %d %d%*c%[^\r\n]",
249 &pIDX->IDX_ht_time_off, &pIDX->IDX_ht_mpy,
250 &pIDX->IDX_ht_off, &pIDX->IDX_lt_time_off,
251 &pIDX->IDX_lt_mpy, &pIDX->IDX_lt_off, &pIDX->IDX_sta_num,
252 &pIDX->IDX_flood_dir, &pIDX->IDX_ebb_dir,
254 return (TC_INDEX_ENTRY_BAD);
255
256 if (abs(pIDX->IDX_ht_time_off) > 1000) // useable?
257 pIDX->IDX_Useable = 0;
258
259 if (abs(pIDX->IDX_flood_dir) > 360) // useable?
260 pIDX->IDX_Useable = 0;
261 if (abs(pIDX->IDX_ebb_dir) > 360) // useable?
262 pIDX->IDX_Useable = 0;
263
264 // Fix up the secondaries which are identical to masters
265 if (pIDX->IDX_ht_mpy == 0.0) pIDX->IDX_ht_mpy = 1.0;
266 if (pIDX->IDX_lt_mpy == 0.0) pIDX->IDX_lt_mpy = 1.0;
267
268 } else {
269 if (9 != sscanf(index_line_buffer,
270 "%*c%d %f %f %d %f %f %d %d%*c%[^\r\n]",
271 &pIDX->IDX_ht_time_off, &pIDX->IDX_ht_mpy,
272 &pIDX->IDX_ht_off, &pIDX->IDX_lt_time_off,
273 &pIDX->IDX_lt_mpy, &pIDX->IDX_lt_off, &pIDX->IDX_sta_num,
274 &pIDX->IDX_ref_file_num, pIDX->IDX_reference_name)) {
275 // Had an error so try alternate with timezone name before ref file
276 // number
277 if (10 != sscanf(index_line_buffer,
278 "%*c%d %f %f %d %f %f %d %s %d%*c%[^\r\n]",
279 &pIDX->IDX_ht_time_off, &pIDX->IDX_ht_mpy,
280 &pIDX->IDX_ht_off, &pIDX->IDX_lt_time_off,
281 &pIDX->IDX_lt_mpy, &pIDX->IDX_lt_off,
282 &pIDX->IDX_sta_num, stz, &pIDX->IDX_ref_file_num,
283 pIDX->IDX_reference_name))
284 return (TC_INDEX_ENTRY_BAD);
285
286 if (NULL != (pIDX->IDX_tzname = (char *)malloc(strlen(stz) + 1)))
287 strcpy(pIDX->IDX_tzname, stz);
288 }
289
290 } // else
291
292 // We only consider 1 reference file per index file
293 pIDX->IDX_ref_file_num = 0;
294 /*
295 if (pIDX->IDX_ref_file_num <= 0)
296 { // Find harmonic reference file number
297 pIDX->IDX_ref_file_num= 0;
298 // Find reference station in index, if no index, it had better
299 be in the first one pIDXh = pIDX_first; while (pIDXh!=NULL &&
300 strcmp(pIDXh->IDX_reference_name,pIDX->IDX_reference_name)) pIDXh =
301 (IDX_entry *)pIDXh->IDX_next;
302
303 // Copy reference station harmonic file number
304 if (pIDXh!=NULL)
305 pIDX->IDX_ref_file_num = pIDXh->IDX_ref_file_num;
306 }
307 */
308 }
309
310 else { // Reference stations have no offsets
311 pIDX->IDX_ht_time_off = pIDX->IDX_lt_time_off = 0;
312 pIDX->IDX_ht_mpy = pIDX->IDX_lt_mpy = 1.0;
313 pIDX->IDX_ht_off = pIDX->IDX_lt_off = 0.0;
314 pIDX->IDX_sta_num = 0;
315 strcpy(pIDX->IDX_reference_name, pIDX->IDX_station_name);
316 }
317
318 if (pIDX->IDX_ht_time_off || pIDX->IDX_ht_off != 0.0 ||
319 pIDX->IDX_lt_off != 0.0 || pIDX->IDX_ht_mpy != 1.0 ||
320 pIDX->IDX_lt_mpy != 1.0)
321 pIDX->have_offsets = 1;
322
323 pIDX->station_tz_offset =
324 0; // ASCII Harmonic data is (always??) corrected to Ref Station TZ
325
326 return (TC_NO_ERROR);
327}
328
329// Load the Harmonic Constant Invariants
330TC_Error_Code TCDS_Ascii_Harmonic::LoadHarmonicConstants(
331 const wxString &data_file_path) {
332 FILE *fp;
333 char linrec[linelen];
334 char junk[80];
335 int a, b;
336
337 free_data();
338
339 fp = fopen(data_file_path.mb_str(), "r");
340 if (NULL == fp) return TC_FILE_NOT_FOUND;
341
342 read_next_line(fp, linrec, 0);
343
344 if (1 != sscanf(linrec, "%d", &num_csts)) goto error;
345 if (num_csts <= 0 ||
346 num_csts >
347 1000000) // 100 % arbitrary roughly twice the harmonic lines number
348 goto error;
349
350 m_cst_speeds = (double *)malloc(num_csts * sizeof(double));
351 m_work_buffer = (double *)malloc(num_csts * sizeof(double));
352
353 /* Load constituent speeds */
354 for (a = 0; a < num_csts; a++) {
355 read_next_line(fp, linrec, 0);
356 sscanf(linrec, "%s %lf", junk, &(m_cst_speeds[a]));
357 m_cst_speeds[a] *= M_PI / 648000; /* Convert to radians per second */
358 }
359
360 /* Get first year for nodes and epochs */
361 read_next_line(fp, linrec, 0);
362 sscanf(linrec, "%d", &m_first_year);
363
364 /* Load epoch table */
365 read_next_line(fp, linrec, 0);
366 if (1 != sscanf(linrec, "%d", &num_epochs)) goto error;
367 if (num_epochs <= 0 || num_epochs > 1000000) goto error;
368
369 m_cst_epochs = (double **)malloc(num_csts * sizeof(double *));
370 for (int i = 0; i < num_csts; i++)
371 m_cst_epochs[i] = (double *)malloc(num_epochs * sizeof(double));
372
373 for (int i = 0; i < num_csts; i++) {
374 if (1 != fscanf(fp, "%s", linrec)) goto error;
375 for (int b = 0; b < num_epochs; b++) {
376 if (1 != fscanf(fp, "%lf", &(m_cst_epochs[i][b]))) goto error;
377 m_cst_epochs[i][b] *= M_PI / 180.0;
378 }
379 }
380
381 /* Sanity check */
382 if (1 != fscanf(fp, "%s", linrec)) goto error;
383 skipnl(fp);
384
385 /* Load node factor table */
386 read_next_line(fp, linrec, 0);
387 if (1 != sscanf(linrec, "%d", &num_nodes)) goto error;
388 if (num_nodes <= 0 || num_nodes > 1000000) goto error;
389
390 m_cst_nodes = (double **)malloc(num_csts * sizeof(double *));
391 for (int a = 0; a < num_csts; a++)
392 m_cst_nodes[a] = (double *)malloc(num_nodes * sizeof(double));
393
394 for (int a = 0; a < num_csts; a++) {
395 int ignore = fscanf(fp, "%s", linrec);
396 for (b = 0; b < num_nodes; b++)
397 ignore = fscanf(fp, "%lf", &(m_cst_nodes[a][b]));
398 }
399
400 fclose(fp);
401
402 return TC_NO_ERROR;
403
404error:
405 fclose(fp);
406 return TC_HARM_FILE_CORRUPT;
407}
408
409TC_Error_Code TCDS_Ascii_Harmonic::LoadHarmonicData(IDX_entry *pIDX) {
410 Station_Data *psd = NULL;
411
412 // Look in the index first
413 if (pIDX->pref_sta_data) return TC_NO_ERROR; // easy
414
415 // Try the member array of "already-looked-at" master stations
416 for (unsigned int i = 0; i < m_msd_array.GetCount(); i++) {
417 psd = &m_msd_array[i];
418 // In the following comparison, it is allowed that the sub-station
419 // reference_name may be
420 // a pre-subset of the master station name.
421 // e.g IDX_refence_name: The Narrows midchannel New York
422 // as found in HARMONIC.IDX
423 // psd_station_name: The Narrows, Midchannel, New York
424 // Harbor, New York Current
425 // as found in HARMONIC
426 if ((!slackcmp(psd->station_name, pIDX->IDX_reference_name)) &&
427 (toupper(pIDX->IDX_type) == psd->station_type)) {
428 pIDX->pref_sta_data = psd; // save for later
429 return TC_NO_ERROR;
430 }
431 }
432
433 // OK, have to read and create from the raw file
434 psd = NULL;
435
436 // If reference station was recently sought, and not found, don't bother
437 // if(!strcmp(pIDX->IDX_reference_name,
438 // plast_reference_not_found->mb_str()))
439 if (m_last_reference_not_found.IsSameAs(
440 wxString(pIDX->IDX_reference_name, wxConvUTF8)))
441 return TC_MASTER_HARMONICS_NOT_FOUND;
442
443 // Clear for this looking
444 m_last_reference_not_found.Clear();
445
446 // Find and load appropriate constituents
447 FILE *fp;
448 char linrec[linelen];
449
450 fp = fopen(m_harmfile_name.mb_str(), "r");
451 if (fp == 0) return TC_MASTER_HARMONICS_NOT_FOUND;
452
453 while (read_next_line(fp, linrec, 1)) {
454 nojunk(linrec);
455 if (slackcmp(linrec, pIDX->IDX_reference_name)) continue;
456
457 // Got the right location, so load the data
458
459 psd = new Station_Data;
460
461 psd->amplitude = (double *)malloc(num_csts * sizeof(double));
462 psd->epoch = (double *)malloc(num_csts * sizeof(double));
463 psd->station_name = (char *)malloc(strlen(linrec) + 1);
464
465 char junk[80];
466 int a;
467 strcpy(psd->station_name, linrec);
468
469 // Establish Station Type
470 wxString caplin(linrec, wxConvUTF8);
471 caplin.MakeUpper();
472 if (caplin.Contains(_T("CURRENT")))
473 psd->station_type = 'C';
474 else
475 psd->station_type = 'T';
476
477 /* Get meridian */
478 read_next_line(fp, linrec, 0);
479 psd->meridian = hhmm2seconds(linrec);
480 psd->zone_offset = 0;
481
482 /* Get tzfile, if present */
483 if (sscanf(nojunk(linrec), "%s %s", junk, psd->tzfile) < 2)
484 strcpy(psd->tzfile, "UTC0");
485
486 /* Get DATUM and units */
487 read_next_line(fp, linrec, 0);
488 if (sscanf(nojunk(linrec), "%lf %s", &(psd->DATUM), psd->unit) < 2)
489 strcpy(psd->unit, "unknown");
490
491 if ((a = findunit(psd->unit)) == -1) {
492 // Nonsense....
493 // strcpy (psd->units_abbrv, psd->unit);
494 // strcpy (psd->units_conv, known_units[a].name);
495 }
496
497 psd->have_BOGUS = (findunit(psd->unit) != -1) &&
498 (known_units[findunit(psd->unit)].type == BOGUS);
499
500 int unit_c;
501 if (psd->have_BOGUS)
502 unit_c = findunit("knots");
503 else
504 unit_c = findunit(psd->unit);
505
506 if (unit_c != -1) {
507 strcpy(psd->units_conv, known_units[unit_c].name);
508 strcpy(psd->units_abbrv, known_units[unit_c].abbrv);
509 }
510
511 /* Get constituents */
512 double loca, loce;
513 for (a = 0; a < num_csts; a++) {
514 read_next_line(fp, linrec, 0);
515 sscanf(linrec, "%s %lf %lf", junk, &loca, &loce);
516 // loc_epoch[a] *= M_PI / 180.0;
517 psd->amplitude[a] = loca;
518 psd->epoch[a] = loce * M_PI / 180.;
519 }
520
521 break;
522 }
523 fclose(fp);
524
525 if (!psd) {
526 m_last_reference_not_found = wxString(pIDX->IDX_reference_name, wxConvUTF8);
527 return TC_MASTER_HARMONICS_NOT_FOUND;
528 } else {
529 m_msd_array.Add(psd); // add it to the member array
530 pIDX->pref_sta_data = psd;
531 return TC_NO_ERROR;
532 }
533}
534
535/*---------------------------------
536 * Low level Index file I/O
537 *-------------------------------*/
538
539long TCDS_Ascii_Harmonic::IndexFileIO(int func, long value) {
540 char *str;
541
542 switch (func) {
543 // Close either/both if open
544 case IFF_CLOSE:
545 if (m_IndexFile) fclose(m_IndexFile);
546 m_IndexFile = NULL;
547 return (0);
548
549 // Open
550 case IFF_OPEN:
551 m_IndexFile = fopen(m_indexfile_name.mb_str(), "rt");
552 if (m_IndexFile == NULL) return (0);
553 return (1);
554
555 // Return file pointer only happens with master file
556 case IFF_TELL:
557 return (ftell(m_IndexFile));
558
559 // Seek
560 case IFF_SEEK:
561 return (fseek(m_IndexFile, value, SEEK_SET));
562
563 // Read until EOF .
564 case IFF_READ:
565 str = fgets(index_line_buffer, 1024, m_IndexFile);
566
567 if (str != NULL)
568 return (1);
569 else
570 return (0);
571 }
572 return (0);
573}
574
575/* Read a line from the harmonics file, skipping comment lines */
576int TCDS_Ascii_Harmonic::read_next_line(FILE *fp, char linrec[linelen],
577 int end_ok) {
578 do {
579 if (!fgets(linrec, linelen, fp)) {
580 if (end_ok)
581 return 0;
582 else {
583 exit(-1);
584 }
585 }
586 } while (linrec[0] == '#' || linrec[0] == '\r' || linrec[0] == '\n');
587 return 1;
588}
589
590/* Remove lingering carriage return, but do nothing else */
591int TCDS_Ascii_Harmonic::skipnl(FILE *fp) {
592 char linrec[linelen];
593 if (NULL == fgets(linrec, linelen, fp)) return 0;
594 return 1;
595}
596
597/* Get rid of trailing garbage in buffer */
598char *TCDS_Ascii_Harmonic::nojunk(char *line) {
599 char *a;
600 a = &(line[strlen(line)]);
601 while (a > line)
602 if (*(a - 1) == '\n' || *(a - 1) == '\r' || *(a - 1) == ' ')
603 *(--a) = '\0';
604 else
605 break;
606 return line;
607}
608
609/* Slackful strcmp; 0 = match. It's case-insensitive and accepts a
610 * prefix instead of the entire string. The second argument is the
611 * one that can be shorter. Second argument can contain '?' as wild
612 * card character.
613 */
614int TCDS_Ascii_Harmonic::slackcmp(char *a, char *b) {
615 int c, cmp, n;
616 n = strlen(b);
617 if ((int)(strlen(a)) < n) return 1;
618 for (c = 0; c < n; c++) {
619 if (b[c] == '?') continue;
620
621 cmp = ((a[c] >= 'A' && a[c] <= 'Z') ? a[c] - 'A' + 'a' : a[c]) -
622 ((b[c] >= 'A' && b[c] <= 'Z') ? b[c] - 'A' + 'a' : b[c]);
623 if (cmp) return cmp;
624 }
625 return 0;
626}
627
628void TCDS_Ascii_Harmonic::free_cst() {
629 free(m_cst_speeds);
630 m_cst_speeds = NULL;
631}
632void TCDS_Ascii_Harmonic::free_nodes() {
633 int a;
634 if (num_csts && m_cst_nodes)
635 for (a = 0; a < num_csts; a++) free(m_cst_nodes[a]);
636 free(m_cst_nodes);
637
638 m_cst_nodes = NULL;
639}
640
641void TCDS_Ascii_Harmonic::free_epochs() {
642 int a;
643 if (num_csts && m_cst_epochs)
644 for (a = 0; a < num_csts; a++) free(m_cst_epochs[a]);
645 free(m_cst_epochs);
646
647 m_cst_epochs = NULL;
648}
649
650/* free harmonics data */
651void TCDS_Ascii_Harmonic::free_data() {
652 free(m_work_buffer);
653 m_work_buffer = NULL;
654 free_nodes();
655 free_epochs();
656 free_cst();
657}
Represents an index entry for tidal and current data.
Definition IDX_entry.h:49
int have_offsets
Flag indicating presence of time/height offsets.
Definition IDX_entry.h:85
int IDX_time_zone
Station timezone offset from UTC (in minutes)
Definition IDX_entry.h:95
char IDX_type
Entry type identifier "TCtcIUu".
Definition IDX_entry.h:61
int num_nodes
Number of nodes in harmonic analysis.
Definition IDX_entry.h:99
float IDX_lt_off
Low tide height offset.
Definition IDX_entry.h:71
char IDX_zone[40]
Geographic zone identifier.
Definition IDX_entry.h:62
TCDataSource * pDataSource
Pointer to the associated data source.
Definition IDX_entry.h:56
float IDX_lt_mpy
Low tide height multiplier.
Definition IDX_entry.h:70
char IDX_reference_name[MAXNAMELEN]
Name of the reference station.
Definition IDX_entry.h:82
int Valid15
Validity flag for 15-minute interval data.
Definition IDX_entry.h:76
int IDX_flood_dir
Flood current direction (in degrees)
Definition IDX_entry.h:73
int IDX_Useable
Flag indicating if the entry is usable.
Definition IDX_entry.h:75
char IDX_station_name[MAXNAMELEN]
Name of the tidal or current station.
Definition IDX_entry.h:63
int station_tz_offset
Offset in seconds to convert from harmonic data (epochs) to the station time zone.
Definition IDX_entry.h:94
int first_year
First year of valid data.
Definition IDX_entry.h:106
double * m_work_buffer
Work buffer for calculations.
Definition IDX_entry.h:105
int IDX_ht_time_off
High tide time offset (in minutes)
Definition IDX_entry.h:66
source_data_t source_data_type
Format of the source data (ASCII or binary)
Definition IDX_entry.h:55
double ** m_cst_nodes
2D array of constituent nodes
Definition IDX_entry.h:103
int IDX_ebb_dir
Ebb current direction (in degrees)
Definition IDX_entry.h:74
double ** m_cst_epochs
2D array of constituent epochs
Definition IDX_entry.h:104
float IDX_ht_mpy
High tide height multiplier.
Definition IDX_entry.h:67
double IDX_lat
Latitude of the station (in degrees, +North)
Definition IDX_entry.h:65
double IDX_lon
Longitude of the station (in degrees, +East)
Definition IDX_entry.h:64
int num_epochs
Number of epochs in harmonic data.
Definition IDX_entry.h:101
double * m_cst_speeds
Array of constituent speeds.
Definition IDX_entry.h:102
Station_Data * pref_sta_data
Pointer to the reference station data.
Definition IDX_entry.h:97
int IDX_lt_time_off
Low tide time offset (in minutes)
Definition IDX_entry.h:69
int IDX_ref_file_num
Reference file number.
Definition IDX_entry.h:81
int num_csts
Number of harmonic constituents.
Definition IDX_entry.h:100
char * IDX_tzname
Timezone name (dynamically allocated)
Definition IDX_entry.h:80
float IDX_ht_off
High tide height offset.
Definition IDX_entry.h:68
int IDX_sta_num
Subordinate station number (UNUSED)
Definition IDX_entry.h:72