OpenCPN Partial API docs
Loading...
Searching...
No Matches
tcds_binary_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/* Declarations for zoneinfo compatibility */
26#include <climits>
27#include <string>
28
30#include "tcmgr.h"
31
32/* Most of these entries are loaded from the tzdata.h include file. That
33 * file was generated from tzdata200c. */
34
35static const char *tz_names[][2] = {
36#include "tzdata.h"
37
38 /* Terminator */
39 {NULL, NULL}};
40
41/* Timelib Time services.
42 * Original XTide source code date: 1997-08-15
43 * Last modified 1998-09-07 by Mike Hopper for WXTide32
44 *
45 * Copyright (C) 1997 David Flater.
46 * Also starring: Geoff Kuenning; Rob Miracle; Dean Pentcheff;
47 * Eric Rosen.
48 *
49 * This program is free software; you can redistribute it and/or modify
50 * it under the terms of the GNU General Public License as published by
51 * the Free Software Foundation; either version 2 of the License, or
52 * (at your option) any later version.
53 *
54 * This program is distributed in the hope that it will be useful,
55 * but WITHOUT ANY WARRANTY; without even the implied warranty of
56 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
57 * GNU General Public License for more details.
58 *
59 * You should have received a copy of the GNU General Public License
60 * along with this program; if not, write to the Free Software
61 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
62 *
63 * Changes by Mike Hopper for WXTide32:
64 * Changed do_timestamp to shorten NT's LONG time zone names just the CAPS
65 * Changed _hpux selector to WIN32 to select HP timezone values.
66 * Added a whole set of remote TZ handler routines, all starting with "tz".
67 */
68
69#ifndef __WXMSW__
70typedef unsigned short WORD;
71typedef long LONG;
72typedef char WCHAR;
73
74typedef struct _SYSTEMTIME {
75 WORD wYear;
76 WORD wMonth;
77 WORD wDayOfWeek;
78 WORD wDay;
79 WORD wHour;
80 WORD wMinute;
81 WORD wSecond;
82 WORD wMilliseconds;
84
85typedef struct _TIME_ZONE_INFORMATION {
86 LONG Bias;
87 WCHAR StandardName[32];
88 SYSTEMTIME StandardDate;
89 LONG StandardBias;
90 WCHAR DaylightName[32];
91 SYSTEMTIME DaylightDate;
92 LONG DaylightBias;
94#else
95#include <windows.h>
96#endif
97
98/*-----------------9/24/2002 4:30PM-----------------
99 * An attempt to get Windoz to work with non-US timezones...
100 * --------------------------------------------------*/
101typedef struct {
103 time_t year_beg;
104 time_t year_end;
105 time_t enter_std;
106 time_t enter_dst;
107 int isdst;
109
110tz_info_entry tz_info_local, tz_info_remote, *tz_info = &tz_info_local;
111
112/*-----------------9/24/2002 8:12AM-----------------
113 * Parse time string in the form [-][hh][:mm][:ss] into seconds.
114 * Returns updated string pointer and signed seconds.
115 * --------------------------------------------------*/
116char *tz_time2sec(char *psrc, long *timesec) {
117 int neg;
118 long temp, mpy;
119 *timesec = 0;
120 mpy = 3600;
121 while (*psrc == ' ') psrc++; /* Skip leading blanks */
122 if (*psrc == '+') psrc++; /* Gobble leading + */
123 if (*psrc == '-') {
124 neg = TRUE;
125 psrc++;
126 } else
127 neg = FALSE;
128
129 do {
130 temp = 0;
131 while (isdigit(*psrc)) temp = temp * 10 + (*(psrc++) - '0');
132
133 *timesec = *timesec + temp * mpy;
134
135 if (*psrc == ':') {
136 mpy /= 60;
137 psrc++;
138 }
139 } while (isdigit(*psrc));
140
141 if (neg) *timesec = 0 - *timesec;
142
143 return (psrc);
144}
145
146/*-----------------9/24/2002 8:16AM-----------------
147 * Parse timezone name string.
148 * Returns string at psrc, updated psrc, and chars copied.
149 * --------------------------------------------------*/
150static char *tz_parse_name(char *psrc, char *pdst, int maxlen) {
151 int nReturn;
152
153 nReturn = 0;
154 while (*psrc == ' ') psrc++; /* Skip leading blanks */
155
156 while (isalpha(*psrc) && nReturn < maxlen) {
157 *(pdst++) = *(psrc++);
158 nReturn++;
159 }
160
161 *pdst = 0;
162 return (psrc);
163}
164
165/*-----------------9/24/2002 8:38AM-----------------
166 * Parse tz rule string into SYSTEMTIME structure.
167 * --------------------------------------------------*/
168static char *tz_parse_rule(char *psrc, SYSTEMTIME *st) {
169 int mol[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
170 long temp, mo;
171 if (*psrc == ',') psrc++; /* Gobble leading comma */
172
173 while (*psrc == ' ') psrc++; /* Skip leading blanks */
174
175 st->wYear = 0;
176 st->wMonth = 0;
177 st->wDay = 0;
178 st->wDayOfWeek = 0;
179 st->wHour = 0;
180 st->wMinute = 0;
181 st->wSecond = 0;
182 st->wMilliseconds = 0;
183
184 if (*psrc == 'J') { /* Julian day (1 <= n <= 365) no leap */
185 psrc++; /* Gobble 'J' */
186 temp = 0;
187 while (isdigit(*psrc)) temp = temp * 10 + (*(psrc++) - '0');
188
189 if (temp < 1 || temp > 365) return (0);
190 temp--;
191 for (mo = 0; temp >= mol[mo]; mo++) temp -= mol[mo];
192 st->wMonth = mo + 1;
193 st->wDay = temp + 1;
194 st->wYear = 1;
195 }
196
197 else if (*psrc == 'M') {
198 psrc++; /* Gobble 'M' */
199
200 temp = 0;
201 while (isdigit(*psrc)) temp = temp * 10 + (*(psrc++) - '0'); /* Get month */
202 if (temp < 1 || temp > 12 || *psrc != '.') return (0);
203 st->wMonth = (unsigned short)temp;
204
205 psrc++; /* Gobble '.' */
206 temp = 0;
207 while (isdigit(*psrc))
208 temp = temp * 10 + (*(psrc++) - '0'); /* Get week number */
209 if (temp < 1 || temp > 5 || *psrc != '.') return (0);
210 st->wDay = (unsigned short)temp;
211
212 psrc++; /* Gobble '.' */
213 temp = 0;
214 while (isdigit(*psrc))
215 temp = temp * 10 + (*(psrc++) - '0'); /* Get day of week number */
216 if (temp < 0 || temp > 6) return (0);
217 st->wDayOfWeek = (unsigned short)temp;
218 }
219
220 if (*psrc == '/') { /* time is specified */
221 psrc++; /* Gobble '/' */
222 psrc = tz_time2sec(psrc, &temp);
223 if (temp < 0 || temp >= 86400) return (0);
224 st->wHour = temp / 3600;
225 temp = temp % 3600;
226 st->wMinute = temp / 60;
227 st->wSecond = temp % 60;
228 }
229 return (psrc);
230}
231
232/*-----------------9/24/2002 3:38PM-----------------
233 * Load tz rule into timezone info data block.
234 * --------------------------------------------------*/
235static void tz_load_rule(char *prule, tz_info_entry *tz_info_remote) {
236 prule = tz_parse_name(prule, (char *)tz_info_remote->tzi.StandardName, 30);
237 prule = tz_time2sec(prule, &tz_info_remote->tzi.Bias);
238 tz_info_remote->tzi.Bias /= 60;
239 tz_info_remote->tzi.StandardBias = 0;
240
241 prule = tz_parse_name(prule, (char *)tz_info_remote->tzi.DaylightName, 30);
242 if (*(char *)tz_info_remote->tzi.DaylightName != '\0') {
243 prule = tz_time2sec(prule, &tz_info_remote->tzi.DaylightBias);
244 tz_info_remote->tzi.DaylightBias /= 60;
245 if (tz_info_remote->tzi.DaylightBias == 0)
246 tz_info_remote->tzi.DaylightBias = -60;
247 else
248 tz_info_remote->tzi.DaylightBias -= tz_info_remote->tzi.Bias;
249
250 if (*prule == ',') {
251 prule = tz_parse_rule(prule, &tz_info_remote->tzi.DaylightDate);
252 if (prule && *prule == ',')
253 tz_parse_rule(prule, &tz_info_remote->tzi.StandardDate);
254 else
255 tz_parse_rule((char *)"M10.5.0/02:00:00",
256 &tz_info_remote->tzi.StandardDate);
257 } else { /* Default is US style tz change */
258 tz_parse_rule((char *)"M4.1.0/02:00:00",
259 &tz_info_remote->tzi.DaylightDate);
260 tz_parse_rule((char *)"M10.5.0/02:00:00",
261 &tz_info_remote->tzi.StandardDate);
262 }
263 } else { /* No DST */
264 tz_info_remote->tzi.DaylightDate.wMonth = 0;
265 tz_info_remote->isdst = 0;
266 }
267}
268
269/* Attempt to load up the local time zone of the location. Moof! */
270static void change_time_zone(const char *tz) {
271 // static char env_string[MAXARGLEN+1];
272 int index;
273
274 if (*tz == ':') tz++; /* Gobble lead-in char */
275 /* Find the translation for the timezone string */
276 index = 0;
277 while (1) {
278 if (tz_names[index][0] == NULL) {
279 tz_info = &tz_info_local;
280 /* Not found. */
281 break;
282 }
283 if (!strcmp(tz_names[index][0], (tz))) {
284 char tz[40];
285 strncpy(tz, tz_names[index][1], 39);
286 tz_load_rule(tz, &tz_info_remote);
287 tz_info = &tz_info_remote;
288 /* Force compute next time this data is used */
289 tz_info->year_beg = 0; // Begin date/time is Jan 1, 1970
290 tz_info->year_end = 0; // End date/time is Jan 1, 1970
291 // sprintf (env_string, "TZ=%s", tz_names[index][1]);
292 break;
293 }
294 index++;
295 }
296}
297
298TCDS_Binary_Harmonic::TCDS_Binary_Harmonic() {
299 m_cst_speeds = NULL;
300 m_cst_nodes = NULL;
301 m_cst_epochs = NULL;
302
303 num_IDX = 0;
304 num_nodes = 0;
305 num_csts = 0;
306 num_epochs = 0;
307
308 m_work_buffer = nullptr;
309 m_cst_epochs = nullptr;
310 m_cst_nodes = nullptr;
311 m_cst_speeds = nullptr;
312}
313
314TCDS_Binary_Harmonic::~TCDS_Binary_Harmonic() {
315 for (int i = 0; i < num_csts; i++) {
316 free(m_cst_nodes[i]);
317 free(m_cst_epochs[i]);
318 }
319 free(m_work_buffer);
320 free(m_cst_epochs);
321 free(m_cst_nodes);
322 free(m_cst_speeds);
323}
324
325TC_Error_Code TCDS_Binary_Harmonic::LoadData(const wxString &data_file_path) {
326 try {
327 if (!open_tide_db(data_file_path.mb_str())) return TC_TCD_FILE_CORRUPT;
328 } catch (...) {
329 return TC_TCD_FILE_CORRUPT;
330 }
331
332 // Build the tables of constituent data
333
334 DB_HEADER_PUBLIC hdr = get_tide_db_header();
335
336 source_ident = wxString(hdr.version, wxConvUTF8);
337
338 num_csts = hdr.constituents;
339 if (0 == num_csts) return TC_GENERIC_ERROR;
340
341 num_nodes = hdr.number_of_years;
342 if (0 == num_nodes) return TC_GENERIC_ERROR;
343
344 // Allocate a working buffer
345 m_work_buffer = (double *)malloc(num_csts * sizeof(double));
346
347 // Constituent speeds
348 m_cst_speeds = (double *)malloc(num_csts * sizeof(double));
349
350 for (int a = 0; a < num_csts; a++) {
351 m_cst_speeds[a] = get_speed(a);
352 m_cst_speeds[a] *= M_PI / 648000; /* Convert to radians per second */
353 }
354
355 // Equilibrium tables by year
356 m_first_year = hdr.start_year;
357 num_epochs = hdr.number_of_years;
358
359 m_cst_epochs = (double **)malloc(num_csts * sizeof(double *));
360 for (int i = 0; i < num_csts; i++)
361 m_cst_epochs[i] = (double *)malloc(num_epochs * sizeof(double));
362
363 for (int i = 0; i < num_csts; i++) {
364 for (int year = 0; year < num_epochs; year++) {
365 m_cst_epochs[i][year] = get_equilibrium(i, year);
366 m_cst_epochs[i][year] *= M_PI / 180.0;
367 }
368 }
369
370 // Node factors
371 unsigned nodes_size = num_csts * sizeof(double *);
372#ifndef __APPLE__
373 assert(nodes_size < PTRDIFF_MAX); // AppleClang barfs, probably a bug.
374#endif
375 m_cst_nodes = (double **)malloc(nodes_size);
376 for (int a = 0; a < num_csts; a++)
377 m_cst_nodes[a] = (double *)malloc(num_nodes * sizeof(double));
378
379 for (int a = 0; a < num_csts; a++) {
380 for (int year = 0; year < num_nodes; year++)
381 m_cst_nodes[a][year] = get_node_factor(a, year);
382 }
383
384 // now load and create the index
385
386 TIDE_RECORD *ptiderec = (TIDE_RECORD *)calloc(sizeof(TIDE_RECORD), 1);
387 for (unsigned int i = 0; i < hdr.number_of_records; i++) {
388 read_tide_record(i, ptiderec);
389
390 num_IDX++; // Keep counting entries for harmonic file stuff
391 IDX_entry *pIDX = new IDX_entry;
393 pIDX->pDataSource = NULL;
394
395 pIDX->Valid15 = 0;
396
397 pIDX->pref_sta_data = NULL; // no reference data yet
398 pIDX->IDX_Useable = 1; // but assume data is OK
399 pIDX->IDX_tzname = NULL;
400
401 pIDX->IDX_lon = ptiderec->header.longitude;
402 pIDX->IDX_lat = ptiderec->header.latitude;
403
404 const char *tz = get_tzfile(ptiderec->header.tzfile);
405 change_time_zone((char *)tz);
406 if (tz_info) pIDX->IDX_time_zone = -tz_info->tzi.Bias;
407
408 strncpy(pIDX->IDX_station_name, ptiderec->header.name, MAXNAMELEN);
409
410 // Extract a "depth" value from name string, if present.
411 // Name string will contain "(depth xx ft)"
412 std::string name(ptiderec->header.name);
413 size_t n = name.find("depth");
414 if (n != std::string::npos) {
415 std::string d = name.substr(n);
416 std::string dp = d.substr(6);
417 size_t nd = dp.find_first_of(' ');
418 std::string sval = dp.substr(0, nd);
419 int depth = std::stoi(sval);
420 pIDX->current_depth = depth;
421 }
422
423 pIDX->IDX_flood_dir = ptiderec->max_direction;
424 pIDX->IDX_ebb_dir = ptiderec->min_direction;
425
426 if (REFERENCE_STATION == ptiderec->header.record_type) {
427 // Establish Station Type
428 wxString caplin(pIDX->IDX_station_name, wxConvUTF8);
429 caplin.MakeUpper();
430 if (caplin.Contains("CURRENT"))
431 pIDX->IDX_type = 'C';
432 else
433 pIDX->IDX_type = 'T';
434
435 int t1 = ptiderec->zone_offset;
436 double zone_offset = (double)(t1 / 100) + ((double)(t1 % 100)) / 60.;
437 // pIDX->IDX_time_zone = t1a;
438
439 pIDX->IDX_ht_time_off = pIDX->IDX_lt_time_off = 0;
440 pIDX->IDX_ht_mpy = pIDX->IDX_lt_mpy = 1.0;
441 pIDX->IDX_ht_off = pIDX->IDX_lt_off = 0.0;
442 pIDX->IDX_ref_dbIndex = ptiderec->header.reference_station; // will be -1
443
444 // build a Station_Data class, and add to member array
445
446 Station_Data *psd = new Station_Data;
447
448 psd->amplitude = (double *)malloc(num_csts * sizeof(double));
449 psd->epoch = (double *)malloc(num_csts * sizeof(double));
450 psd->station_name = (char *)malloc(ONELINER_LENGTH);
451
452 strncpy(psd->station_name, ptiderec->header.name, MAXNAMELEN);
453 psd->station_type = pIDX->IDX_type;
454
455 // Get meridian, which is seconds difference from UTC, not figuring DST,
456 // so that New York is always (-300 * 60)
457 psd->meridian = -(tz_info->tzi.Bias * 60);
458 psd->zone_offset = zone_offset;
459
460 // Get units
461 strncpy(psd->unit, get_level_units(ptiderec->level_units), 40 - 1);
462 psd->unit[40 - 1] = '\0';
463
464 psd->have_BOGUS = (findunit(psd->unit) != -1) &&
465 (known_units[findunit(psd->unit)].type == BOGUS);
466
467 int unit_c;
468 if (psd->have_BOGUS)
469 unit_c = findunit("knots");
470 else
471 unit_c = findunit(psd->unit);
472
473 if (unit_c != -1) {
474 strncpy(psd->units_conv, known_units[unit_c].name,
475 sizeof(psd->units_conv) - 1);
476 strncpy(psd->units_abbrv, known_units[unit_c].abbrv,
477 sizeof(psd->units_abbrv) - 1);
478 } else {
479 memcpy(psd->units_conv, psd->unit, 40 - 1);
480 psd->units_conv[40 - 1] = '\0';
481 memcpy(psd->units_abbrv, psd->unit, 20 - 1);
482 psd->units_abbrv[20 - 1] = '\0';
483 }
484
485 // Get constituents
486 for (int a = 0; a < num_csts; a++) {
487 psd->amplitude[a] = ptiderec->amplitude[a];
488 psd->epoch[a] = ptiderec->epoch[a] * M_PI / 180.;
489 }
490
491 psd->DATUM = ptiderec->datum_offset;
492
493 m_msd_array.Add(psd); // add it to the member array
494 pIDX->pref_sta_data = psd;
495 pIDX->IDX_ref_dbIndex = i;
496 pIDX->have_offsets = 0;
497 } else if (SUBORDINATE_STATION == ptiderec->header.record_type) {
498 // Establish Station Type
499 wxString caplin(pIDX->IDX_station_name, wxConvUTF8);
500 caplin.MakeUpper();
501 if (caplin.Contains("CURRENT"))
502 pIDX->IDX_type = 'c';
503 else
504 pIDX->IDX_type = 't';
505
506 int t1 = ptiderec->max_time_add;
507 double t1a = (double)(t1 / 100) + ((double)(t1 % 100)) / 60.;
508 t1a *= 60; // Minutes
509 pIDX->IDX_ht_time_off = t1a;
510 pIDX->IDX_ht_mpy = ptiderec->max_level_multiply;
511 if (0. == pIDX->IDX_ht_mpy) pIDX->IDX_ht_mpy = 1.0;
512 pIDX->IDX_ht_off = ptiderec->max_level_add;
513
514 t1 = ptiderec->min_time_add;
515 t1a = (double)(t1 / 100) + ((double)(t1 % 100)) / 60.;
516 t1a *= 60; // Minutes
517 pIDX->IDX_lt_time_off = t1a;
518 pIDX->IDX_lt_mpy = ptiderec->min_level_multiply;
519 if (0. == pIDX->IDX_lt_mpy) pIDX->IDX_lt_mpy = 1.0;
520 pIDX->IDX_lt_off = ptiderec->min_level_add;
521
522 pIDX->IDX_ref_dbIndex = ptiderec->header.reference_station;
523 // strncpy(pIDX->IDX_reference_name, ptiderec->header.name,
524 // MAXNAMELEN);
525
526 if (pIDX->IDX_ht_time_off || pIDX->IDX_ht_off != 0.0 ||
527 pIDX->IDX_lt_off != 0.0 || pIDX->IDX_ht_mpy != 1.0 ||
528 pIDX->IDX_lt_mpy != 1.0)
529 pIDX->have_offsets = 1;
530 }
531
532 m_IDX_array.Add(pIDX);
533 }
534
535 // Mark the index entries individually with invariant harmonic constants
536 unsigned int max_index = GetMaxIndex();
537 for (unsigned int i = 0; i < max_index; i++) {
538 IDX_entry *pIDX = GetIndexEntry(i);
539 if (pIDX) {
540 pIDX->num_nodes = num_nodes;
541 pIDX->num_csts = num_csts;
542 pIDX->num_epochs = num_epochs;
543 pIDX->m_cst_speeds = m_cst_speeds;
544 pIDX->m_cst_nodes = m_cst_nodes;
545 pIDX->m_cst_epochs = m_cst_epochs;
546 pIDX->first_year = m_first_year;
547 pIDX->m_work_buffer = m_work_buffer;
548 }
549 }
550 free(ptiderec);
551
552 return TC_NO_ERROR;
553}
554
555IDX_entry *TCDS_Binary_Harmonic::GetIndexEntry(int n_index) {
556 return &m_IDX_array[n_index];
557}
558
559TC_Error_Code TCDS_Binary_Harmonic::LoadHarmonicData(IDX_entry *pIDX) {
560 // Find the indicated Master station
561 if (!strlen(pIDX->IDX_reference_name)) {
562 strncpy(pIDX->IDX_reference_name, get_station(pIDX->IDX_ref_dbIndex),
563 MAXNAMELEN - 1);
564 pIDX->IDX_reference_name[MAXNAMELEN - 1] = '\0';
565
566 // TIDE_RECORD *ptiderec = (TIDE_RECORD *)calloc(sizeof(TIDE_RECORD),
567 // 1); read_tide_record (pIDX->IDX_ref_dbIndex, ptiderec); free(
568 // ptiderec );
569
570 IDX_entry *pIDX_Ref = &m_IDX_array.Item(pIDX->IDX_ref_dbIndex);
571 Station_Data *pRefSta = pIDX_Ref->pref_sta_data;
572 pIDX->pref_sta_data = pRefSta;
573 pIDX->station_tz_offset =
574 -pRefSta->meridian + (pRefSta->zone_offset * 3600);
575 }
576 return TC_NO_ERROR;
577}
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
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_ref_dbIndex
Database index of the reference station.
Definition idx_entry.h:82
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
int current_depth
Depth for current stations.
Definition idx_entry.h:108
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 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
@ SOURCE_TYPE_BINARY_HARMONIC
Binary harmonic source type.
Definition idx_entry.h:40
TCDataFactory loading data from binary file.
Tide and Current Manager @TODO Add original author copyright.