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