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