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