taxis.c 36 KB
Newer Older
Uwe Schulzweida's avatar
Uwe Schulzweida committed
1
2
3
4
5
6
7
8
9
#if defined (HAVE_CONFIG_H)
#  include "config.h"
#endif

#include <string.h>

#include "dmemory.h"

#include "cdi.h"
10
#include "error.h"
11
#include "taxis.h"
12
#include "cdi_cksum.h"
13
#include "cdi_int.h"
14
#include "calendar.h"
Deike Kleberg's avatar
Deike Kleberg committed
15
#include "namespace.h"
16
#include "serialize.h"
17
#include "resource_handle.h"
18
#include "resource_unpack.h"
Uwe Schulzweida's avatar
Uwe Schulzweida committed
19

Uwe Schulzweida's avatar
Uwe Schulzweida committed
20
21
extern int cdiDefaultCalendar;

22
23
static int DefaultTimeType = TAXIS_ABSOLUTE;
static int DefaultTimeUnit = TUNIT_HOUR;
Uwe Schulzweida's avatar
Uwe Schulzweida committed
24
25
26
27
28
29
30
31
32
33


char *Timeunits[] = {
  "undefined",
  "seconds",
  "minutes",
  "hours",
  "days",
  "months",
  "years",
Deike Kleberg's avatar
Deike Kleberg committed
34
  "quarters",
35
36
37
  "3hours",
  "6hours",
  "12hours",
Uwe Schulzweida's avatar
Uwe Schulzweida committed
38
39
40
};


41
42
static int    taxisCompareP    ( void * taxisptr1, void * taxisptr2 );
static void   taxisDestroyP    ( void * taxisptr );
Thomas Jahns's avatar
Thomas Jahns committed
43
static void   taxisPrintKernel(taxis_t *taxisptr, FILE * fp);
44
static int    taxisGetPackSize ( void * taxisptr, void *context );
45
static void   taxisPack        ( void * taxisptr, void *buf, int size,
46
				 int *position, void *context );
47
static int    taxisTxCode      ( void );
48

49
50
51
const resOps taxisOps = {
  taxisCompareP,
  taxisDestroyP,
Thomas Jahns's avatar
Thomas Jahns committed
52
  (void (*)(void *, FILE *))taxisPrintKernel,
53
54
55
  taxisGetPackSize,
  taxisPack,
  taxisTxCode
56
};
57
58
59
60
61


static int  TAXIS_Debug = 0;   /* If set to 1, debugging */


Uwe Schulzweida's avatar
Uwe Schulzweida committed
62
63
64
65
66
67
68
69
70
71
72
73
74
char *tunitNamePtr(int unitID)
{
  char *name;
  int size = sizeof(Timeunits)/sizeof(char *);

  if ( unitID > 0 && unitID < size )
    name = Timeunits[unitID];
  else
    name = Timeunits[0];

  return (name);
}

75
#if 0
76
static
Uwe Schulzweida's avatar
Uwe Schulzweida committed
77
void taxis_defaults(void)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
78
79
80
81
82
83
{
  char *timeunit;

  timeunit = getenv("TIMEUNIT");
  if ( timeunit )
    {
Deike Kleberg's avatar
Deike Kleberg committed
84
      if ( strcmp(timeunit, "minutes") == 0 )
Uwe Schulzweida's avatar
Uwe Schulzweida committed
85
	DefaultTimeUnit = TUNIT_MINUTE;
Deike Kleberg's avatar
Deike Kleberg committed
86
      else if ( strcmp(timeunit, "hours") == 0 )
Uwe Schulzweida's avatar
Uwe Schulzweida committed
87
	DefaultTimeUnit = TUNIT_HOUR;
88
89
90
91
92
93
      else if ( strcmp(timeunit, "3hours") == 0 )
	DefaultTimeUnit = TUNIT_3HOURS;
      else if ( strcmp(timeunit, "6hours") == 0 )
	DefaultTimeUnit = TUNIT_6HOURS;
      else if ( strcmp(timeunit, "12hours") == 0 )
	DefaultTimeUnit = TUNIT_12HOURS;
Deike Kleberg's avatar
Deike Kleberg committed
94
      else if ( strcmp(timeunit, "days") == 0 )
Uwe Schulzweida's avatar
Uwe Schulzweida committed
95
	DefaultTimeUnit = TUNIT_DAY;
Deike Kleberg's avatar
Deike Kleberg committed
96
      else if ( strcmp(timeunit, "months") == 0 )
Uwe Schulzweida's avatar
Uwe Schulzweida committed
97
	DefaultTimeUnit = TUNIT_MONTH;
Deike Kleberg's avatar
Deike Kleberg committed
98
      else if ( strcmp(timeunit, "years") == 0 )
Uwe Schulzweida's avatar
Uwe Schulzweida committed
99
100
	DefaultTimeUnit = TUNIT_YEAR;
      else
101
	Warning("Unsupported TIMEUNIT %s!", timeunit);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
102
    }
Uwe Schulzweida's avatar
Uwe Schulzweida committed
103
}
104
#endif
Uwe Schulzweida's avatar
Uwe Schulzweida committed
105

106
static
107
void taxisDefaultValue(taxis_t* taxisptr)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
108
{
109
110
111
112
113
114
115
  taxisptr->self        = CDI_UNDEFID;
  taxisptr->used        = FALSE;
  taxisptr->type        = DefaultTimeType;
  taxisptr->vdate       = 0;
  taxisptr->vtime       = 0;
  taxisptr->rdate       = CDI_UNDEFID;
  taxisptr->rtime       = 0;
116
117
  taxisptr->fdate       = CDI_UNDEFID;
  taxisptr->ftime       = 0;
118
119
120
121
122
123
124
125
126
127
  taxisptr->calendar    = cdiDefaultCalendar;
  taxisptr->unit        = DefaultTimeUnit;
  taxisptr->numavg      = 0;
  taxisptr->has_bounds  = FALSE;
  taxisptr->vdate_lb    = 0;
  taxisptr->vtime_lb    = 0;
  taxisptr->vdate_ub    = 0;
  taxisptr->vtime_ub    = 0;
  taxisptr->name        = NULL;
  taxisptr->longname    = NULL;
128
  taxisptr->climatology = FALSE;
Uwe Schulzweida's avatar
Uwe Schulzweida committed
129
}
Uwe Schulzweida's avatar
Uwe Schulzweida committed
130

131
132
static taxis_t *
taxisNewEntry(cdiResH resH)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
133
{
134
  taxis_t *taxisptr = xmalloc(sizeof (taxis_t));
Uwe Schulzweida's avatar
Uwe Schulzweida committed
135

136
137
138
139
140
141
142
143
  taxisDefaultValue(taxisptr);
  if (resH == CDI_UNDEFID)
    taxisptr->self = reshPut(taxisptr, &taxisOps);
  else
    {
      taxisptr->self = resH;
      reshReplace(resH, taxisptr, &taxisOps);
    }
144
145

  return (taxisptr);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
146
147
}

148
static
149
void taxisInit (void)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
150
{
151
152
  static int taxisInitialized = 0;
  char *env;
Uwe Schulzweida's avatar
Uwe Schulzweida committed
153

154
  if ( taxisInitialized ) return;
Uwe Schulzweida's avatar
Uwe Schulzweida committed
155

156
  taxisInitialized = 1;
Uwe Schulzweida's avatar
Uwe Schulzweida committed
157

158
159
  env = getenv("TAXIS_DEBUG");
  if ( env ) TAXIS_Debug = atoi(env);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
160
161
}

162
#if 0
163
164
static
void taxis_copy(taxis_t *taxisptr2, taxis_t *taxisptr1)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
165
{
166
  int taxisID2 = taxisptr2->self;
167
  memcpy(taxisptr2, taxisptr1, sizeof(taxis_t));
Uwe Schulzweida's avatar
Uwe Schulzweida committed
168
169
  taxisptr2->self = taxisID2;
}
170
#endif
Uwe Schulzweida's avatar
Uwe Schulzweida committed
171

172
173
static
void taxis_check_ptr(const char *caller, taxis_t *taxisptr)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
174
175
{
  if ( taxisptr == NULL )
176
    Errorc("taxis undefined!");
Uwe Schulzweida's avatar
Uwe Schulzweida committed
177
178
179
}

/*
180
@Function  taxisCreate
181
@Title     Create a Time axis
Uwe Schulzweida's avatar
Uwe Schulzweida committed
182

183
@Prototype int taxisCreate(int taxistype)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
184
@Parameter
Uwe Schulzweida's avatar
Uwe Schulzweida committed
185
186
    @Item  taxistype  The type of the Time axis, one of the set of predefined CDI time axis types.
                      The valid CDI time axis types are @func{TAXIS_ABSOLUTE} and @func{TAXIS_RELATIVE}.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
187
188

@Description
189
The function @func{taxisCreate} creates a Time axis.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
190
191

@Result
192
@func{taxisCreate} returns an identifier to the Time axis.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
193

Uwe Schulzweida's avatar
Uwe Schulzweida committed
194
195
196
197
198
199
200
201
202
203
204
@Example
Here is an example using @func{taxisCreate} to create a relative T-axis
with a standard calendar.

@Source
#include "cdi.h"
   ...
int taxisID;
   ...
taxisID = taxisCreate(TAXIS_RELATIVE);
taxisDefCalendar(taxisID, CALENDAR_STANDARD);
205
taxisDefRdate(taxisID, 19850101);
206
taxisDefRtime(taxisID, 120000);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
207
208
   ...
@EndSource
Uwe Schulzweida's avatar
Uwe Schulzweida committed
209
210
@EndFunction
*/
211
int taxisCreate(int taxistype)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
212
213
{
  if ( CDI_Debug )
214
    Message("taxistype: %d", taxistype);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
215

216
217
  taxisInit ();

218
  taxis_t *taxisptr = taxisNewEntry(CDI_UNDEFID);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
219
  taxisptr->type = taxistype;
Uwe Schulzweida's avatar
Uwe Schulzweida committed
220

221
222
  int taxisID = taxisptr->self;

Uwe Schulzweida's avatar
Uwe Schulzweida committed
223
  if ( CDI_Debug )
224
    Message("taxisID: %d", taxisID);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
225
226
227
228

  return (taxisID);
}

Uwe Schulzweida's avatar
Uwe Schulzweida committed
229
230
static
void taxisDestroyKernel( taxis_t * taxisptr )
231
232
233
{
  taxis_check_ptr(__func__, taxisptr);

234
  int id = taxisptr->self;
235
236
237

  free ( taxisptr );

238
  reshRemove ( id, &taxisOps );
239
240
}

241
242
243
244
245
246
/*
@Function  taxisDestroy
@Title     Destroy a Time axis

@Prototype void taxisDestroy(int taxisID)
@Parameter
247
    @Item  taxisID  Time axis ID, from a previous call to @func{taxisCreate}
248
249
250
251
252

@EndFunction
*/
void taxisDestroy(int taxisID)
{
253
  taxis_t *taxisptr = ( taxis_t * ) reshGetVal ( taxisID, &taxisOps );
254

255
256
257
  if ( taxisptr->name     ) free(taxisptr->name);
  if ( taxisptr->longname ) free(taxisptr->longname);

258
259
  taxisDestroyKernel ( taxisptr );
}
260

Uwe Schulzweida's avatar
Uwe Schulzweida committed
261
262

void taxisDestroyP( void * taxisptr )
263
264
{
  taxisDestroyKernel (( taxis_t * ) taxisptr );
265
266
267
}


Uwe Schulzweida's avatar
Uwe Schulzweida committed
268
269
int taxisDuplicate(int taxisID1)
{
270
  taxis_t *taxisptr1 = ( taxis_t * ) reshGetVal ( taxisID1, &taxisOps );
Uwe Schulzweida's avatar
Uwe Schulzweida committed
271

272
  taxis_t *taxisptr2 = taxisNewEntry(CDI_UNDEFID);
273
  if ( ! taxisptr2 ) Error("No memory");
Uwe Schulzweida's avatar
Uwe Schulzweida committed
274

275
  int taxisID2 = taxisptr2->self;
Uwe Schulzweida's avatar
Uwe Schulzweida committed
276
277

  if ( CDI_Debug )
278
    Message("taxisID2: %d", taxisID2);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
279

Uwe Schulzweida's avatar
Uwe Schulzweida committed
280
  ptaxisCopy(taxisptr2, taxisptr1);
281
282
  if ( taxisptr1->name     ) taxisptr2->name = strdup(taxisptr1->name);
  if ( taxisptr1->longname ) taxisptr2->longname = strdup(taxisptr1->longname);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
283

284
  // taxisptr2->has_bounds = FALSE;
Uwe Schulzweida's avatar
Uwe Schulzweida committed
285

Uwe Schulzweida's avatar
Uwe Schulzweida committed
286
287
288
289
290
291
  return (taxisID2);
}


void taxisDefType(int taxisID, int type)
{
292
  if ( reshGetStatus ( taxisID, &taxisOps ) == RESH_CLOSED )
293
    {
294
      Warning("%s", "Operation not executed." );
295
296
297
      return;
    }

298
  taxis_t *taxisptr = ( taxis_t * ) reshGetVal ( taxisID, &taxisOps );
Uwe Schulzweida's avatar
Uwe Schulzweida committed
299

300
  taxis_check_ptr(__func__, taxisptr);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
301

Uwe Schulzweida's avatar
Uwe Schulzweida committed
302
  taxisptr->type = type;
Uwe Schulzweida's avatar
Uwe Schulzweida committed
303
304
305
306
307
308
309
310
}

/*
@Function  taxisDefVdate
@Title     Define the verification date

@Prototype void taxisDefVdate(int taxisID, int vdate)
@Parameter
Uwe Schulzweida's avatar
Uwe Schulzweida committed
311
    @Item  taxisID  Time axis ID, from a previous call to @fref{taxisCreate}
Uwe Schulzweida's avatar
Uwe Schulzweida committed
312
313
314
    @Item  vdate    Verification date (YYYYMMDD)

@Description
315
The function @func{taxisDefVdate} defines the verification date of a Time axis.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
316
317
318
319
320

@EndFunction
*/
void taxisDefVdate(int taxisID, int vdate)
{
321
  taxis_t *taxisptr = ( taxis_t * ) reshGetVal ( taxisID, &taxisOps );
Uwe Schulzweida's avatar
Uwe Schulzweida committed
322

323
  taxis_check_ptr(__func__, taxisptr);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
324
325

  taxisptr->vdate = vdate;
Uwe Schulzweida's avatar
Uwe Schulzweida committed
326
327
328
329
330
331
332
333
}

/*
@Function  taxisDefVtime
@Title     Define the verification time

@Prototype void taxisDefVtime(int taxisID, int vtime)
@Parameter
Uwe Schulzweida's avatar
Uwe Schulzweida committed
334
    @Item  taxisID  Time axis ID, from a previous call to @fref{taxisCreate}
335
    @Item  vtime    Verification time (hhmmss)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
336
337

@Description
338
The function @func{taxisDefVtime} defines the verification time of a Time axis.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
339
340
341
342
343

@EndFunction
*/
void taxisDefVtime(int taxisID, int vtime)
{
344
  taxis_t *taxisptr = ( taxis_t * ) reshGetVal ( taxisID, &taxisOps );
Uwe Schulzweida's avatar
Uwe Schulzweida committed
345

346
  taxis_check_ptr(__func__, taxisptr);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
347

Uwe Schulzweida's avatar
Uwe Schulzweida committed
348
  taxisptr->vtime = vtime;
Uwe Schulzweida's avatar
Uwe Schulzweida committed
349
350
351
352
353
354
355
356
}

/*
@Function  taxisDefRdate
@Title     Define the reference date

@Prototype void taxisDefRdate(int taxisID, int rdate)
@Parameter
Uwe Schulzweida's avatar
Uwe Schulzweida committed
357
    @Item  taxisID  Time axis ID, from a previous call to @fref{taxisCreate}
Uwe Schulzweida's avatar
Uwe Schulzweida committed
358
359
360
    @Item  rdate    Reference date (YYYYMMDD)

@Description
361
The function @func{taxisDefRdate} defines the reference date of a Time axis.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
362
363
364
365
366

@EndFunction
*/
void taxisDefRdate(int taxisID, int rdate)
{
367
  if ( reshGetStatus ( taxisID, &taxisOps ) == RESH_CLOSED )
368
    {
369
      Warning("%s", "Operation not executed.");
370
371
372
      return;
    }

373
  taxis_t *taxisptr = ( taxis_t * ) reshGetVal ( taxisID, &taxisOps );
Uwe Schulzweida's avatar
Uwe Schulzweida committed
374

375
  taxis_check_ptr(__func__, taxisptr);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
376
377

  taxisptr->rdate = rdate;
Uwe Schulzweida's avatar
Uwe Schulzweida committed
378
379
380
381
382
383
384
385
}

/*
@Function  taxisDefRtime
@Title     Define the reference time

@Prototype void taxisDefRtime(int taxisID, int rtime)
@Parameter
Uwe Schulzweida's avatar
Uwe Schulzweida committed
386
    @Item  taxisID  Time axis ID, from a previous call to @fref{taxisCreate}
387
    @Item  rtime    Reference time (hhmmss)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
388
389

@Description
390
The function @func{taxisDefRtime} defines the reference time of a Time axis.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
391
392
393
394
395

@EndFunction
*/
void taxisDefRtime(int taxisID, int rtime)
{
396
  if ( reshGetStatus ( taxisID, &taxisOps ) == RESH_CLOSED )
397
    {
398
      Warning("%s", "Operation not executed.");
399
400
401
      return;
    }

402
  taxis_t *taxisptr = ( taxis_t * ) reshGetVal ( taxisID, &taxisOps );
Uwe Schulzweida's avatar
Uwe Schulzweida committed
403

404
  taxis_check_ptr(__func__, taxisptr);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
405

Uwe Schulzweida's avatar
Uwe Schulzweida committed
406
  taxisptr->rtime = rtime;
Uwe Schulzweida's avatar
Uwe Schulzweida committed
407
408
}

409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
/*
@Function  taxisDefFdate
@Title     Define the forecast reference date

@Prototype void taxisDefFdate(int taxisID, int fdate)
@Parameter
    @Item  taxisID  Time axis ID, from a previous call to @fref{taxisCreate}
    @Item  fdate    Forecast reference date (YYYYMMDD)

@Description
The function @func{taxisDefFdate} defines the forecast reference date of a Time axis.

@EndFunction
*/
void taxisDefFdate(int taxisID, int fdate)
{
425
  if ( reshGetStatus ( taxisID, &taxisOps ) == RESH_CLOSED )
426
427
428
429
430
    {
      Warning("%s", "Operation not executed.");
      return;
    }

431
  taxis_t *taxisptr = ( taxis_t * ) reshGetVal ( taxisID, &taxisOps );
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453

  taxis_check_ptr(__func__, taxisptr);

  taxisptr->fdate = fdate;
}

/*
@Function  taxisDefFtime
@Title     Define the forecast reference time

@Prototype void taxisDefFtime(int taxisID, int ftime)
@Parameter
    @Item  taxisID  Time axis ID, from a previous call to @fref{taxisCreate}
    @Item  ftime    Forecast reference time (hhmmss)

@Description
The function @func{taxisDefFtime} defines the forecast reference time of a Time axis.

@EndFunction
*/
void taxisDefFtime(int taxisID, int ftime)
{
454
  if ( reshGetStatus ( taxisID, &taxisOps ) == RESH_CLOSED )
455
456
457
458
459
    {
      Warning("%s", "Operation not executed.");
      return;
    }

460
  taxis_t *taxisptr = ( taxis_t * ) reshGetVal ( taxisID, &taxisOps );
461
462
463
464
465
466

  taxis_check_ptr(__func__, taxisptr);

  taxisptr->ftime = ftime;
}

Uwe Schulzweida's avatar
Uwe Schulzweida committed
467
468
469
470
471
472
/*
@Function  taxisDefCalendar
@Title     Define the calendar

@Prototype void taxisDefCalendar(int taxisID, int calendar)
@Parameter
Uwe Schulzweida's avatar
Uwe Schulzweida committed
473
    @Item  taxisID  Time axis ID, from a previous call to @fref{taxisCreate}
474
    @Item  calendar The type of the calendar, one of the set of predefined CDI calendar types.
475
                    The valid CDI calendar types are @func{CALENDAR_STANDARD}, @func{CALENDAR_PROLEPTIC},
476
                    @func{CALENDAR_360DAYS}, @func{CALENDAR_365DAYS} and @func{CALENDAR_366DAYS}.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
477
478

@Description
479
The function @func{taxisDefCalendar} defines the calendar of a Time axis.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
480
481
482
483
484

@EndFunction
*/
void taxisDefCalendar(int taxisID, int calendar)
{
485
  if ( reshGetStatus ( taxisID, &taxisOps ) == RESH_CLOSED )
486
    {
487
      Warning("%s", "Operation not executed.");
488
489
490
      return;
    }

491
  taxis_t *taxisptr = ( taxis_t * ) reshGetVal ( taxisID, &taxisOps );
Uwe Schulzweida's avatar
Uwe Schulzweida committed
492

493
  taxis_check_ptr(__func__, taxisptr);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
494
495

  taxisptr->calendar = calendar;
Uwe Schulzweida's avatar
Uwe Schulzweida committed
496
497
498
499
500
}


void taxisDefTunit(int taxisID, int unit)
{
501
  if ( reshGetStatus ( taxisID, &taxisOps ) == RESH_CLOSED )
502
    {
503
      Warning("%s", "Operation not executed.");
504
505
506
      return;
    }

507
  taxis_t *taxisptr = ( taxis_t * ) reshGetVal ( taxisID, &taxisOps );
Uwe Schulzweida's avatar
Uwe Schulzweida committed
508

509
  taxis_check_ptr(__func__, taxisptr);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
510

Uwe Schulzweida's avatar
Uwe Schulzweida committed
511
  taxisptr->unit = unit;
Uwe Schulzweida's avatar
Uwe Schulzweida committed
512
513
}

514

Uwe Schulzweida's avatar
Uwe Schulzweida committed
515
516
void taxisDefNumavg(int taxisID, int numavg)
{
517
  if ( reshGetStatus ( taxisID, &taxisOps ) == RESH_CLOSED )
518
    {
519
      Warning("%s", "Operation not executed.");
520
521
522
      return;
    }

523
  taxis_t *taxisptr = ( taxis_t * ) reshGetVal ( taxisID, &taxisOps );
Uwe Schulzweida's avatar
Uwe Schulzweida committed
524

525
  taxis_check_ptr(__func__, taxisptr);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
526
527

  taxisptr->numavg = numavg;
Uwe Schulzweida's avatar
Uwe Schulzweida committed
528
529
530
531
532
533
534
535
}

/*
The type of the time axis, one of the set of predefined CDI time types.
The valid CDI time types are TAXIS_ABSOLUTE and TAXIS_RELATIVE.
*/
int taxisInqType(int taxisID)
{
536
  taxis_t *taxisptr = ( taxis_t * ) reshGetVal ( taxisID, &taxisOps );
Uwe Schulzweida's avatar
Uwe Schulzweida committed
537

538
  taxis_check_ptr(__func__, taxisptr);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
539

Uwe Schulzweida's avatar
Uwe Schulzweida committed
540
  return (taxisptr->type);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
541
542
543
}


Uwe Schulzweida's avatar
Uwe Schulzweida committed
544
545
int taxisHasBounds(int taxisID)
{
546
  taxis_t *taxisptr = ( taxis_t * ) reshGetVal ( taxisID, &taxisOps );
Uwe Schulzweida's avatar
Uwe Schulzweida committed
547

548
  taxis_check_ptr(__func__, taxisptr);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
549
550
551
552
553

  return (taxisptr->has_bounds);
}


554
555
void taxisDeleteBounds(int taxisID)
{
556
  if ( reshGetStatus ( taxisID, &taxisOps ) == RESH_CLOSED )
557
    {
558
      Warning("%s", "Operation not executed.");
559
560
561
      return;
    }

562
  taxis_t *taxisptr = ( taxis_t * ) reshGetVal ( taxisID, &taxisOps );
563
564
565
566
567
568
569

  taxis_check_ptr(__func__, taxisptr);

  taxisptr->has_bounds = FALSE;
}


Uwe Schulzweida's avatar
Uwe Schulzweida committed
570
void taxisCopyTimestep(int taxisID2, int taxisID1)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
571
{
572
573
  taxis_t *taxisptr1 = ( taxis_t * ) reshGetVal ( taxisID1, &taxisOps );
  taxis_t *taxisptr2 = ( taxis_t * ) reshGetVal ( taxisID2, &taxisOps );
Uwe Schulzweida's avatar
Uwe Schulzweida committed
574

575
576
577
  taxis_check_ptr(__func__, taxisptr1);
  taxis_check_ptr(__func__, taxisptr2);

578
  reshLock ();
Uwe Schulzweida's avatar
Uwe Schulzweida committed
579

580
581
582
  taxisptr2->rdate = taxisptr1->rdate;
  taxisptr2->rtime = taxisptr1->rtime;

Uwe Schulzweida's avatar
Uwe Schulzweida committed
583
584
585
586
  taxisptr2->vdate = taxisptr1->vdate;
  taxisptr2->vtime = taxisptr1->vtime;

  if ( taxisptr2->has_bounds )
Uwe Schulzweida's avatar
Uwe Schulzweida committed
587
    {
Uwe Schulzweida's avatar
Uwe Schulzweida committed
588
589
590
591
      taxisptr2->vdate_lb = taxisptr1->vdate_lb;
      taxisptr2->vtime_lb = taxisptr1->vtime_lb;
      taxisptr2->vdate_ub = taxisptr1->vdate_ub;
      taxisptr2->vtime_ub = taxisptr1->vtime_ub;
Uwe Schulzweida's avatar
Uwe Schulzweida committed
592
593
    }

594
  reshUnlock ();
595
}
Uwe Schulzweida's avatar
Uwe Schulzweida committed
596
597
598
599
600
601
602

/*
@Function  taxisInqVdate
@Title     Get the verification date

@Prototype int taxisInqVdate(int taxisID)
@Parameter
603
    @Item  taxisID  Time axis ID, from a previous call to @fref{taxisCreate} or @fref{vlistInqTaxis}
Uwe Schulzweida's avatar
Uwe Schulzweida committed
604
605

@Description
606
The function @func{taxisInqVdate} returns the verification date of a Time axis.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
607
608

@Result
609
@func{taxisInqVdate} returns the verification date.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
610
611
612
613
614

@EndFunction
*/
int taxisInqVdate(int taxisID)
{
615
  taxis_t *taxisptr = ( taxis_t * ) reshGetVal ( taxisID, &taxisOps );
Uwe Schulzweida's avatar
Uwe Schulzweida committed
616

617
  taxis_check_ptr(__func__, taxisptr);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
618

Uwe Schulzweida's avatar
Uwe Schulzweida committed
619
  return (taxisptr->vdate);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
620
621
622
}


Uwe Schulzweida's avatar
Uwe Schulzweida committed
623
624
void taxisInqVdateBounds(int taxisID, int *vdate_lb, int *vdate_ub)
{
625
  taxis_t *taxisptr = ( taxis_t * ) reshGetVal ( taxisID, &taxisOps );
Uwe Schulzweida's avatar
Uwe Schulzweida committed
626

627
  taxis_check_ptr(__func__, taxisptr);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
628
629
630
631
632
633

  *vdate_lb = taxisptr->vdate_lb;
  *vdate_ub = taxisptr->vdate_ub;
}


634
635
void taxisDefVdateBounds(int taxisID, int vdate_lb, int vdate_ub)
{
636
  if ( reshGetStatus ( taxisID, &taxisOps ) == RESH_CLOSED )
637
    {
638
      Warning("%s", "Operation not executed.");
639
640
641
      return;
    }

642
  taxis_t *taxisptr = ( taxis_t * ) reshGetVal ( taxisID, &taxisOps );
643

644
  taxis_check_ptr(__func__, taxisptr);
645
646
647

  taxisptr->vdate_lb = vdate_lb;
  taxisptr->vdate_ub = vdate_ub;
648

649
650
651
  taxisptr->has_bounds = TRUE;
}

Uwe Schulzweida's avatar
Uwe Schulzweida committed
652
653
654
655
656
657
/*
@Function  taxisInqVtime
@Title     Get the verification time

@Prototype int taxisInqVtime(int taxisID)
@Parameter
658
    @Item  taxisID  Time axis ID, from a previous call to @fref{taxisCreate} or @fref{vlistInqTaxis}
Uwe Schulzweida's avatar
Uwe Schulzweida committed
659
660

@Description
661
The function @func{taxisInqVtime} returns the verification time of a Time axis.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
662
663

@Result
664
@func{taxisInqVtime} returns the verification time.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
665
666
667
668
669

@EndFunction
*/
int taxisInqVtime(int taxisID)
{
670
  taxis_t *taxisptr = ( taxis_t * ) reshGetVal ( taxisID, &taxisOps );
Uwe Schulzweida's avatar
Uwe Schulzweida committed
671

672
  taxis_check_ptr(__func__, taxisptr);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
673
674

  return (taxisptr->vtime);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
675
676
677
}


Uwe Schulzweida's avatar
Uwe Schulzweida committed
678
679
void taxisInqVtimeBounds(int taxisID, int *vtime_lb, int *vtime_ub)
{
680
  taxis_t *taxisptr = ( taxis_t * ) reshGetVal ( taxisID, &taxisOps );
Uwe Schulzweida's avatar
Uwe Schulzweida committed
681

682
  taxis_check_ptr(__func__, taxisptr);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
683
684
685
686
687
688

  *vtime_lb = taxisptr->vtime_lb;
  *vtime_ub = taxisptr->vtime_ub;
}


689
690
void taxisDefVtimeBounds(int taxisID, int vtime_lb, int vtime_ub)
{
691
  if ( reshGetStatus ( taxisID, &taxisOps ) == RESH_CLOSED )
692
    {
693
      Warning("%s", "Operation not executed.");
694
695
696
      return;
    }

697
  taxis_t *taxisptr = ( taxis_t * ) reshGetVal ( taxisID, &taxisOps );
698

699
  taxis_check_ptr(__func__, taxisptr);
700
701
702

  taxisptr->vtime_lb = vtime_lb;
  taxisptr->vtime_ub = vtime_ub;
703

704
705
706
  taxisptr->has_bounds = TRUE;
}

Uwe Schulzweida's avatar
Uwe Schulzweida committed
707
708
709
710
711
712
/*
@Function  taxisInqRdate
@Title     Get the reference date

@Prototype int taxisInqRdate(int taxisID)
@Parameter
713
    @Item  taxisID  Time axis ID, from a previous call to @fref{taxisCreate} or @fref{vlistInqTaxis}
Uwe Schulzweida's avatar
Uwe Schulzweida committed
714
715

@Description
716
The function @func{taxisInqRdate} returns the reference date of a Time axis.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
717
718

@Result
719
@func{taxisInqRdate} returns the reference date.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
720
721
722
723
724

@EndFunction
*/
int taxisInqRdate(int taxisID)
{
725
  taxis_t *taxisptr = ( taxis_t * ) reshGetVal ( taxisID, &taxisOps );
Uwe Schulzweida's avatar
Uwe Schulzweida committed
726

727
  taxis_check_ptr(__func__, taxisptr);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
728

Uwe Schulzweida's avatar
Uwe Schulzweida committed
729
  if ( taxisptr->rdate == -1 )
Uwe Schulzweida's avatar
Uwe Schulzweida committed
730
    {
Uwe Schulzweida's avatar
Uwe Schulzweida committed
731
732
      taxisptr->rdate = taxisptr->vdate;
      taxisptr->rtime = taxisptr->vtime;
Uwe Schulzweida's avatar
Uwe Schulzweida committed
733
734
    }

Uwe Schulzweida's avatar
Uwe Schulzweida committed
735
  return (taxisptr->rdate);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
736
737
738
739
740
741
742
743
}

/*
@Function  taxisInqRtime
@Title     Get the reference time

@Prototype int taxisInqRtime(int taxisID)
@Parameter
744
    @Item  taxisID  Time axis ID, from a previous call to @fref{taxisCreate} or @fref{vlistInqTaxis}
Uwe Schulzweida's avatar
Uwe Schulzweida committed
745
746

@Description
747
The function @func{taxisInqRtime} returns the reference time of a Time axis.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
748
749

@Result
750
@func{taxisInqRtime} returns the reference time.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
751
752
753
754
755

@EndFunction
*/
int taxisInqRtime(int taxisID)
{
756
  taxis_t *taxisptr = ( taxis_t * ) reshGetVal ( taxisID, &taxisOps );
Uwe Schulzweida's avatar
Uwe Schulzweida committed
757

758
  taxis_check_ptr(__func__, taxisptr);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
759

Uwe Schulzweida's avatar
Uwe Schulzweida committed
760
  if ( taxisptr->rdate == -1 )
Uwe Schulzweida's avatar
Uwe Schulzweida committed
761
    {
Uwe Schulzweida's avatar
Uwe Schulzweida committed
762
763
      taxisptr->rdate = taxisptr->vdate;
      taxisptr->rtime = taxisptr->vtime;
Uwe Schulzweida's avatar
Uwe Schulzweida committed
764
765
    }

Uwe Schulzweida's avatar
Uwe Schulzweida committed
766
  return (taxisptr->rtime);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
767
768
}

769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
/*
@Function  taxisInqFdate
@Title     Get the forecast reference date

@Prototype int taxisInqFdate(int taxisID)
@Parameter
    @Item  taxisID  Time axis ID, from a previous call to @fref{taxisCreate} or @fref{vlistInqTaxis}

@Description
The function @func{taxisInqFdate} returns the forecast reference date of a Time axis.

@Result
@func{taxisInqFdate} returns the forecast reference date.

@EndFunction
*/
int taxisInqFdate(int taxisID)
{
787
  taxis_t *taxisptr = ( taxis_t * ) reshGetVal ( taxisID, &taxisOps );
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817

  taxis_check_ptr(__func__, taxisptr);

  if ( taxisptr->fdate == -1 )
    {
      taxisptr->fdate = taxisptr->vdate;
      taxisptr->ftime = taxisptr->vtime;
    }

  return (taxisptr->fdate);
}

/*
@Function  taxisInqFtime
@Title     Get the forecast reference time

@Prototype int taxisInqFtime(int taxisID)
@Parameter
    @Item  taxisID  Time axis ID, from a previous call to @fref{taxisCreate} or @fref{vlistInqTaxis}

@Description
The function @func{taxisInqFtime} returns the forecast reference time of a Time axis.

@Result
@func{taxisInqFtime} returns the forecast reference time.

@EndFunction
*/
int taxisInqFtime(int taxisID)
{
818
  taxis_t *taxisptr = ( taxis_t * ) reshGetVal ( taxisID, &taxisOps );
819
820
821
822
823
824
825
826
827
828
829
830

  taxis_check_ptr(__func__, taxisptr);

  if ( taxisptr->fdate == -1 )
    {
      taxisptr->fdate = taxisptr->vdate;
      taxisptr->ftime = taxisptr->vtime;
    }

  return (taxisptr->ftime);
}

Uwe Schulzweida's avatar
Uwe Schulzweida committed
831
832
833
834
835
836
/*
@Function  taxisInqCalendar
@Title     Get the calendar

@Prototype int taxisInqCalendar(int taxisID)
@Parameter
837
    @Item  taxisID  Time axis ID, from a previous call to @fref{taxisCreate} or @fref{vlistInqTaxis}
Uwe Schulzweida's avatar
Uwe Schulzweida committed
838
839

@Description
840
The function @func{taxisInqCalendar} returns the calendar of a Time axis.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
841
842

@Result
843
@func{taxisInqCalendar} returns the type of the calendar,
Uwe Schulzweida's avatar
Uwe Schulzweida committed
844
one of the set of predefined CDI calendar types.
845
The valid CDI calendar types are @func{CALENDAR_STANDARD}, @func{CALENDAR_PROLEPTIC},
846
@func{CALENDAR_360DAYS}, @func{CALENDAR_365DAYS} and @func{CALENDAR_366DAYS}.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
847
848
849
850
851

@EndFunction
*/
int taxisInqCalendar(int taxisID)
{
852
  taxis_t *taxisptr = ( taxis_t * ) reshGetVal ( taxisID, &taxisOps );
Uwe Schulzweida's avatar
Uwe Schulzweida committed
853

854
  taxis_check_ptr(__func__, taxisptr);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
855
856

  return (taxisptr->calendar);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
857
858
859
860
861
}


int taxisInqTunit(int taxisID)
{
862
  taxis_t *taxisptr = ( taxis_t * ) reshGetVal ( taxisID, &taxisOps );
Uwe Schulzweida's avatar
Uwe Schulzweida committed
863

864
  taxis_check_ptr(__func__, taxisptr);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
865

Uwe Schulzweida's avatar
Uwe Schulzweida committed
866
  return (taxisptr->unit);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
867
868
869
870
871
}


int taxisInqNumavg(int taxisID)
{
872
  taxis_t *taxisptr = ( taxis_t * ) reshGetVal ( taxisID, &taxisOps );
Uwe Schulzweida's avatar
Uwe Schulzweida committed
873

874
  taxis_check_ptr(__func__, taxisptr);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
875
876

  return (taxisptr->numavg);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
877
878
879
}


880
taxis_t *taxisPtr(int taxisID)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
881
{
882
  taxis_t *taxisptr = ( taxis_t * ) reshGetVal ( taxisID, &taxisOps );
Uwe Schulzweida's avatar
Uwe Schulzweida committed
883

884
  taxis_check_ptr(__func__, taxisptr);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
885

Uwe Schulzweida's avatar
Uwe Schulzweida committed
886
  return (taxisptr);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
887
888
889
}


Uwe Schulzweida's avatar
Uwe Schulzweida committed
890
void cdiDecodeTimevalue(int timeunit, double timevalue, int *days, int *secs)
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
{
  static int lwarn = TRUE;

  if ( timeunit == TUNIT_MINUTE )
    {
      timevalue *= 60;
      timeunit = TUNIT_SECOND;
    }
  else if ( timeunit == TUNIT_HOUR )
    {
      timevalue /= 24;
      timeunit = TUNIT_DAY;
    }

  if ( timeunit == TUNIT_SECOND )
    {
      *days = (int) (timevalue/86400);
      *secs = (int) (timevalue - *days*86400.);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
909
      if ( *secs < 0 ) { *days -= 1; *secs += 86400; };
910
911
912
913
914
915
916
917
918
919
920
      /*
      {
	double cval = *days*86400. + *secs;
	if ( cval != timevalue )
	  printf("TUNIT_SECOND error: %g %g %d %d\n", timevalue, cval, *days, *secs);
      }
      */
    }
  else if ( timeunit == TUNIT_DAY )
    {
      *days = (int) timevalue;
921
      *secs = (int) ((timevalue - *days)*86400 + 0.5);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
922
      if ( *secs < 0 ) { *days -= 1; *secs += 86400; };
923
924
925
926
927
928
929
930
931
932
933
934
      /*
      {
	double cval = *days + *secs/86400.;
	if ( cval != timevalue )
	  printf("TUNIT_DAY error: %g %g %d %d\n", timevalue, cval, *days, *secs);
      }
      */
    }
  else
    {
      if ( lwarn )
	{
935
	  Warning("timeunit %s unsupported!", tunitNamePtr(timeunit));
936
937
938
939
940
	  lwarn = FALSE;
	}
    }
}

941
static
Uwe Schulzweida's avatar
Uwe Schulzweida committed
942
void cdiEncodeTimevalue(int days, int secs, int timeunit, double *timevalue)
943
944
945
946
947
948
949
{
  static int lwarn = TRUE;

  if ( timeunit == TUNIT_SECOND )
    {
      *timevalue = days*86400. + secs;
    }
950
951
  else if ( timeunit == TUNIT_MINUTE ||
	    timeunit == TUNIT_QUARTER )
952
953
954
    {
      *timevalue = days*1440. + secs/60.;
    }
955
956
957
958
  else if ( timeunit == TUNIT_HOUR   ||
	    timeunit == TUNIT_3HOURS ||
	    timeunit == TUNIT_6HOURS ||
	    timeunit == TUNIT_12HOURS )
959
960
961
962
963
964
965
966
967
968
969
    {
      *timevalue = days*24. + secs/3600.;
    }
  else if ( timeunit == TUNIT_DAY )
    {
      *timevalue = days + secs/86400.;
    }
  else
    {
      if ( lwarn )
	{
970
	  Warning("timeunit %s unsupported!", tunitNamePtr(timeunit));
971
972
973
974
975
	  lwarn = FALSE;
	}
    }
}

976
int days_per_month(int calendar, int year, int month);
977

978
void timeval2vtime(double timevalue, taxis_t *taxis, int *vdate, int *vtime)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
979
{
980
  int year, month, day, hour, minute, second;
Uwe Schulzweida's avatar
Uwe Schulzweida committed
981
982
  int rdate, rtime;
  int timeunit;
983
984
  int calendar;
  int julday, secofday, days, secs;
Uwe Schulzweida's avatar
Uwe Schulzweida committed
985
986
987
988
989
990
991
992
993

  *vdate = 0;
  *vtime = 0;

  timeunit = (*taxis).unit;
  calendar = (*taxis).calendar;

  rdate  = (*taxis).rdate;
  rtime  = (*taxis).rtime;
994
995
996

  if ( rdate == 0 && rtime == 0 && DBL_IS_EQUAL(timevalue, 0.) ) return;

Uwe Schulzweida's avatar
Uwe Schulzweida committed
997
998
  cdiDecodeDate(rdate, &year, &month, &day);
  cdiDecodeTime(rtime, &hour, &minute, &second);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
999

1000
1001
1002
1003
1004
1005
  if ( timeunit == TUNIT_MONTH && calendar == CALENDAR_360DAYS )
    {
      timeunit = TUNIT_DAY;
      timevalue *= 30;
    }

Uwe Schulzweida's avatar
Uwe Schulzweida committed
1006
1007
  if ( timeunit == TUNIT_MONTH || timeunit == TUNIT_YEAR )
    {
1008
1009
      int nmon, dpm;
      double fmon;
1010

1011
      if ( timeunit == TUNIT_YEAR ) timevalue *= 12;
Uwe Schulzweida's avatar
Uwe Schulzweida committed
1012

1013
1014
      nmon = (int) timevalue;
      fmon = timevalue - nmon;
Uwe Schulzweida's avatar
Uwe Schulzweida committed
1015

1016
      month += nmon;
Uwe Schulzweida's avatar
Uwe Schulzweida committed
1017

1018
1019
      while ( month > 12 ) { month -= 12; year++; }
      while ( month <  1 ) { month += 12; year--; }
1020

1021
1022
1023
      dpm = days_per_month(calendar, year, month);
      timeunit = TUNIT_DAY;
      timevalue = fmon*dpm;
Uwe Schulzweida's avatar
Uwe Schulzweida committed
1024
    }
1025

1026
  encode_caldaysec(calendar, year, month, day, hour, minute, second, &julday, &secofday);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
1027

1028
  cdiDecodeTimevalue(timeunit, timevalue, &days, &secs);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
1029

1030
  julday_add(days, secs, &julday, &secofday);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
1031

1032
  decode_caldaysec(calendar, julday, secofday, &year, &month, &day, &hour, &minute, &second);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
1033

Uwe Schulzweida's avatar
Uwe Schulzweida committed
1034
1035
  *vdate = cdiEncodeDate(year, month, day);
  *vtime = cdiEncodeTime(hour, minute, second);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
1036
1037
1038
}


1039
double vtime2timeval(int vdate, int vtime, taxis_t *taxis)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
1040
1041
{
  int ryear, rmonth;
1042
  int year, month, day, hour, minute, second;
Uwe Schulzweida's avatar
Uwe Schulzweida committed
1043
  int rdate, rtime;
1044
  double value = 0;
Uwe Schulzweida's avatar
Uwe Schulzweida committed
1045
  int timeunit;
1046
  int timeunit0;
1047
1048
  int calendar;
  int julday1, secofday1, julday2, secofday2, days, secs;
Uwe Schulzweida's avatar
Uwe Schulzweida committed
1049
1050