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