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