netCDF-SDK

Hier wird beschrieben, wie die von UniPlot verwendeten Messdaten-Dateien mit C-Programmen (oder Programmen mit C-Anbindung) gelesen und geschrieben werden können.

Inhalt

Die folgenden Dateien müssen mit Ihrer Anwendung installiert werden. Diese Dateien können Sie einem installierten UniPlot entnehmen (R2018 oder später). Es können die 32-Bit oder die 64-Bit Version oder beide Versionen getrennt verwendet werden. Die Datei license.txt finden Sie im Verzeichnis UniPlot\samples\SDK\netCDF.

rs_updf_nc.dll
rs_updf.dll
rs_xlib.dll
zlib.dll
license.txt

Sonstige Dateien aus dem Verzeichnis UniPlot\samples\SDK\netCDF:

ncread.c            - Ein einfaches Beispiel-Programm zum Lesen.
ncwrite.c           - Ein einfaches Beispiel-Programm zum Schreiben.
win32\rs_updf_nc.lib      - Die 32-Bit Import-Library.
x64\rs_updf_nc.lib  - Die 64-Bit Import-Library.
up-nc.h             - Die Header-Datei.
map-data.nc2        - Einfache Beispiel-Datei.

Einige Anmerkungen

  • Das UniPlot-netCDF-SDK verwendet die API von netCDF 2.4.

    http://www.unidata.ucar.edu/software/netcdf/old_docs/really_old/guide_toc.html

  • Die von UniPlot verwendeten netCDF-Dateien können in drei verschiedenen Formaten vorliegen:

    1. Das „klassische“ netCDF-Format wie im Link oben beschrieben. Dateigröße maximal 2 GBytes. Die ersten drei Zeichen der Datei sind „CDF“.

    2. Eine speziell für UniPlot abgewandelte Form mit Intel-Byteorder (Little Endian). Diese Dateien können von Standard-netCDF-Librarys nicht gelesen werden. Dateigröße ebenfalls maximal 2 GBytes. Die ersten drei Zeichen der Datei sind „CDH“.

    3. Ein spezielles UniPlot-Format, dass von dem netCDF-Format nur noch die API verwendet. Es unterstützt Kompression und schnelles Löschen und Zufügen von Kanälen und Attributen. Die ersten drei Zeichen der Datei sind „UPD“. Diese Dateien sind wie ein Dateisystem aufgebaut. Die Informationen befinden sich in Sektoren, in denen sich Daten und Inhaltsverzeichnisse befinden. Es hat einige Ähnlichkeiten mit dem auf HDF aufbauenden netCDF-4, ist aber ein eigenes, unabhängiges Dateiformat und wesentlich einfacher als netCDF-4.

      Dateigröße maximal 1 TB, Kanallänge in dieser Version 2^31-1 Datenpunkte.

  • Falls Sie einen Microsoft C-Compiler verwenden, können Sie das Beispielprogramm mit:

    cl ncread.c x64\rs_updf_nc.lib
    

    übersetzen. Die Pfade für den C-Compiler müssen richtig gesetzt sein.

    Starten Sie das Programm mit:

    ncread map-data.nc2
    

    Es gibt die ersten 10 Datenpunkte aller Kanäle der Datendatei aus. Testdateien in verschiedenen Formaten können Sie mit UniPlot erstellen. Schalten Sie dazu aber in Extras=>Weitere Optionen von UniPlot Verzögertes Laden von Datenkanälen ab.

  • Anstatt die Import-Library rs_updf_nc.lib zu verwenden, können Sie die Funktionen auch über LoadLibrary, (LoadLibraryEx, GetModuleHandle) und GetProcAddress dynamisch laden.

  • Bei einigen Ursprungsformaten (z. B. INCA-MDF) konvertiert UniPlot die Dateien optional nicht komplett in einem Zug in das netCDF-Format. In diesen Dateien sind zwar alle globalen Attribute, die Definition der Kanäle sowie die Attribute der Kanäle enthalten, jedoch sind die Daten nicht von allen Kanälen enthalten.

    Ob die Daten des Kanals vorhanden sind, kann über das Kanal-Attribut _nc_dl_loaded gleich 0 oder 1 festgestellt werden. Über das globale Attribut _nc_dl_loadcount kann die Anzahl der geladenen Kanäle ermittelt werden.

Beispiele

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
/*
 compile: cl ncwrite.c rs_updf_nc.lib
*/

#include <stdlib.h>
#include <stdio.h>
#include "up-nc.h"

int error(char *msg)
{
    fprintf(stderr, "%s", msg);
    exit(1);
}

void main(int argc, char *argv[])
{
    test();
    exit(0);
}

int test()
{
    int ncid;
    double *p;
    int dimid;
    int i, varid, start, count, len;
    double scale = 1.0, offset = 0.0;

    ncid = nccreate("test1.nc2");
    if (ncid == -1) {
        error("cannot open output file");
    }

    ncredef(ncid);

    // Globale Attribute

    varid = -1;
    ncattput(ncid, varid, "Origin", NC_CHAR, 1, "");
    ncattput(ncid, varid, "Source", NC_CHAR, 1, "");
    len = strlen("FEV Software and Testing Solutions GmbH (www.uniplot.com)")+1;
    ncattput(ncid, varid, "Creator", NC_CHAR, len, "FEV Software and Testing Solutions GmbH (www.uniplot.com)");
    ncattput(ncid, varid, "Date", NC_CHAR, strlen("15 Nov 2013")+1, "15 Nov 2013");
    ncattput(ncid, varid, "Time", NC_CHAR, strlen("09:24:20")+1, "09:24:20");

    // Dimensionen
    dimid = ncdimdef(ncid, "n", 100);

    // Kanäle und Kanal-Attribute
    varid = ncvardef(ncid, "var1", NC_DOUBLE, 1, &dimid);
    ncattput(ncid, varid, "units", NC_CHAR, strlen("1/min")+1, "1/min");

    ncattput(ncid, varid, "scale_factor", NC_DOUBLE, 1, &scale);
    ncattput(ncid, varid, "add_offset", NC_DOUBLE, 1, &offset);

    ncendef(ncid);

    // Nun die Kanäle schreiben

    p = (double*) malloc(100*sizeof(double));
    for (i = 0; i < 100; i++) p[i] = (double) i;
    start = 0;
    count = 100;
    ncvarput(ncid, varid, &start, &count, p);
    free(p);
    ncclose(ncid);
    return 0;
}

Schreiben eines String-Kanals

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// compile: cl test_nc_string.c rs_updf_nc.lib

#include <stdlib.h>
#include <stdio.h>
#include "up-nc.h"

#define MIN(a,b) (((a)<(b))?(a):(b))

void main()
{
    int ncid, varid, n1, len1, start[2], count[2], dimids[2];
    char *s;

    ncid = nccreate("d:\\test2.nc2");

    // Kanallänge 10.000
    n1 = ncdimdef(ncid, "n", 10000);

    // Max. Anzahl an Zeichen in einer Zeichenkette, z. B. 32
    len1 = ncdimdef(ncid, "len", 32);

    dimids[0] = n1;
    dimids[1] = len1;
    varid = ncvardef(ncid, "StringChannel", NC_CHAR, 2, dimids);
    ncendef(ncid);

    start[0] = 0;
    start[1] = 0;
    count[0] = 1;
    count[1] = 5;
    ncvarput(ncid, varid, start, count, "Hallo");

    s = "Dies ist ein längerer String";
    start[0] = 10;
    start[1] = 0;
    count[0] = 1;
    count[1] = MIN(32, strlen(s));
    ncvarput(ncid, varid, start, count, s);

    ncclose(ncid);
}

Lesen einer Datei:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
/*
 compile:

 cl ncread.c rs_updf_nc.lib

 See also

 * readme.txt

 * http://www.unidata.ucar.edu/software/netcdf/old_docs/really_old/guide_toc.html

*/

#include <stdlib.h>
#include <stdio.h>
#include "up-nc.h"

int error(char *msg)
{
    fprintf(stderr, "%s", msg);
    exit(0);
}

int att_print(int ncid, int varid, int attnum)
{
    char name[MAX_NC_NAME+1];
    int i, data_type, len;
    double *dVal;
    float *fVal;
    int *iVal;
    short *sVal;
    char *cVal;

    ncattname(ncid, varid, attnum, name);
    ncattinq(ncid, varid, name, &data_type, &len);
    switch (data_type) {
    case NC_CHAR:
        cVal = (char*) malloc(len * sizeof(char));
        ncattget(ncid, varid, name, cVal);
        printf("%s = ", name);
        for (i = 0; i < len; i++) {
            printf("%c", cVal[i]);
        }
        printf("\n");
        free(cVal);
        break;

    case NC_BYTE:
        cVal = (char*) malloc(len * sizeof(char));
        ncattget(ncid, varid, name, cVal);
        printf("%s = ", name);
        for (i = 0; i < len; i++) {
            printf("%d ", cVal[i]);
        }
        printf("\n");
        free(cVal);
        break;

    case NC_SHORT:
        sVal = (short*) malloc(len * sizeof(short));
        ncattget(ncid, varid, name, sVal);
        printf("%s = ", name);
        for (i = 0; i < len; i++) {
            printf("%d ", sVal[i]);
        }
        printf("\n");
        free(sVal);
        break;

    case NC_LONG:
        iVal = (int*) malloc(len * sizeof(int));
        ncattget(ncid, varid, name, iVal);
        printf("%s = ", name);
        for (i = 0; i < len; i++) {
            printf("%d ", iVal[i]);
        }
        printf("\n");
        free(iVal);
        break;

    case NC_FLOAT:
        fVal = (float*) malloc(len * sizeof(float));
        ncattget(ncid, varid, name, fVal);
        printf("%s = ", name);
        for (i = 0; i < len; i++) {
            printf("%f ", fVal[i]);
        }
        printf("\n");
        free(fVal);
        break;

    case NC_DOUBLE:
        dVal = (double*) malloc(len * sizeof(double));
        ncattget(ncid, varid, name, dVal);
        printf("%s = ", name);
        for (i = 0; i < len; i++) {
            printf("%lf ", dVal[i]);
        }
        printf("\n");
        free(dVal);
        break;
    }
    return 0;
}

void main(int argc, char *argv[])
{
    int ncid;
    int nvars, ngatts, data_type, ndims, natts, length, length2;
    int dimids[MAX_NC_DIMS];
    int i, j;
    char name[136];
    double *vals;
    float *fvals;
    char *cvals;
    unsigned char *bvals;
    short *svals;
    int *ivals;
    int ret, start;
    int st[2], le[2];

    int max_len = 10;

    if (argc != 2) {
        error("usage: ncread nc-file-name");
    }

    ncid = ncopen(argv[1], 0);
    if (ncid == -1) {
        error("cannot open input file");
    }
    ncinquire(ncid, 0, &nvars, &ngatts, 0);

    printf("ngatts=%d\n", ngatts);
    for (i = 0; i < ngatts; i++) {
        att_print(ncid, -1, i);
    }

    printf("\nnvars=%d\n", nvars);

    for (i = 0; i < nvars; i++) {
        ncvarinq(ncid, i, name, &data_type, &ndims, dimids, &natts);

        printf("%s\n", name);

        printf("natts=%d\n", natts);
        for (j = 0; j < natts; j++) {
            att_print(ncid, i, j);
        }

        if (ndims != 1 && data_type != NC_CHAR) {
            printf("Variable %s wird im Beispielprog. ignoriert wg. Anzahl Dimensionen != 1", name);
            continue;
        }
        switch (data_type) {
        case NC_CHAR:
            if (ndims != 2) {
                printf("Variable %s wird im Beispielprog. ignoriert wg. Anzahl Dimensionen != 2", name);
                continue;
            }
            ncdiminq(ncid, dimids[0], 0, &length);
            ncdiminq(ncid, dimids[1], 0, &length2);
            cvals = (char*) malloc(length2 * sizeof(char));
            if (cvals == 0) {
                error("out of memory");
            }
            printf("len=%d\n", length);
            for (j = 0; j < min(max_len, length); j++) {
                st[0] = j;
                st[1] = 0;
                le[0] = 1;
                le[1] = length2;
                ret = ncvarget(ncid, i, st, le, cvals);
                printf("%s\n", cvals);
            }
            free(cvals);
            break;

        case NC_BYTE:
            ncdiminq(ncid, dimids[0], 0, &length);
            bvals = (unsigned char*) malloc(length * sizeof(unsigned char));
            if (bvals == 0) {
                error("out of memory");
            }
            start = 0;
            ret = ncvarget(ncid, i, &start, &length, bvals);
            printf("len=%d\n", length);
            for (j = 0; j < min(max_len, length); j++) {
                printf("%d\n", bvals[j]);
            }
            free(bvals);
            break;

        case NC_SHORT:
            ncdiminq(ncid, dimids[0], 0, &length);
            svals = (short*) malloc(length * sizeof(short));
            if (svals == 0) {
                error("out of memory");
            }
            start = 0;
            ret = ncvarget(ncid, i, &start, &length, svals);
            printf("len=%d\n", length);
            for (j = 0; j < min(max_len, length); j++) {
                printf("%d\n", svals[j]);
            }
            free(svals);
            break;

        case NC_LONG:
            ncdiminq(ncid, dimids[0], 0, &length);
            ivals = (int*) malloc(length * sizeof(int));
            if (ivals == 0) {
                error("out of memory");
            }
            start = 0;
            ret = ncvarget(ncid, i, &start, &length, ivals);
            printf("len=%d\n", length);
            for (j = 0; j < min(max_len, length); j++) {
                printf("%d\n", ivals[j]);
            }
            free(ivals);
            break;

        case NC_FLOAT:
            ncdiminq(ncid, dimids[0], 0, &length);
            fvals = (float*) malloc(length * sizeof(float));
            if (fvals == 0) {
                error("out of memory");
            }
            start = 0;
            ret = ncvarget(ncid, i, &start, &length, fvals);
            printf("len=%d\n", length);
            for (j = 0; j < min(max_len, length); j++) {
                printf("%f\n", fvals[j]);
            }
            free(fvals);
            break;

        case NC_DOUBLE:
            ncdiminq(ncid, dimids[0], 0, &length);
            vals = (double*) malloc(length * sizeof(double));
            if (vals == 0) {
                error("out of memory");
            }
            start = 0;
            ret = ncvarget(ncid, i, &start, &length, vals);
            printf("len=%d\n", length);
            for (j = 0; j < min(max_len, length); j++) {
                printf("%lf\n", vals[j]);
            }
            free(vals);
            break;
        }
    }

    ncclose(ncid);
}

Konventionen

  • Kanalnamen und Attributnamen sollten aus folgenden Zeichen bestehen: _[a-z][A-Z][0-9], Zahlen nicht am Anfang des Namens, Länge maximal 128 Zeichen. Die folgenden 10 Sonderzeichen sind in Kanalnamen und Attributnamen ebenfalls zulässig: . - + $ # ~ ! ^ & %.

  • Kanal- und Attributnamen sollten nicht mit einem Unterstrich _ oder den Zeichen nc_` beginnen. Diese Namen sind reserviert für interne Zwecke (z. B. werden die Attribute nc_min, nc_monotone, _FillValue, _nc_dl_loadinfo intern verwendet).

  • Kanalnamen müssen eindeutig sein, die Anzahl an Kanälen ist auf 20.000 pro Datei beschränkt.

  • Missing Values: Sind einzelne Werte eines Kanals ungültig, sollte bei NC_DOUBLE- und NC_FLOAT-Kanälen der Zahlenwert 1e10 für die ungültigen Werte geschrieben werden. Bei Integer-Kanälen sollte das Kanal-Attribut missing_value geschrieben werden.

  • Zeitkanäle mit Sekunden und Sekundenbruchteilen sollten als NC_DOUBLE (8 Bytes) geschrieben werden, Daten die durch Transformation aus 16Bit-Integer-Kanälen entstanden sind, können als NC_FLOAT (4 Bytes) geschrieben werden.

  • Zeitkanäle die neben der Zeit noch das Datum enthalten, sollten als NC_DOUBLE geschrieben werden. UniPlot verwendet das gleiche Format wie Microsoft-Excel:

    uniplot_time = unix_time / 86400 + 25569
    
    Unix-Datum sind die Sekunden seit 1.1.1970 00:00:00.
    Excelzeit sind die Tage seit dem 31.12.1899
    
    86400: Sekunden eines Tages (24*60*60)
    25569: Tage zwischen 31.12.1899 und 1.1.1970
    

    Für den Kanal muss ausserdem das Attribut datatype mit einem der Werte „date“, „time“ oder „datetime“ geschrieben werden.

    Normalerweise sind Zeitkanäle aber normale NC_DOUBLE-Kanäle mit der Einheit Sekunde. Der Beginn der Messung wird dann als Datum/Zeit in globale Attribute geschrieben.

  • Stringkanäle sind Matrizen vom Typ NC_CHAR. Die zweite Dimension sollte nur so lang wie nötig sein, da alle Strings mit Nullen aufgefüllt werden. Siehe Beispiel oben.

  • Folgende globale Attribute (Attribute für die ganze Datei) sollten vorhanden sein:

    Attribut-Name Datentyp Bedeutung
    Origin NC_CHAR Name aus der die netCDF-Datei entstanden ist, z. B. c:\test-data\test.mdf4, kann leer gelassen werden, wenn es keine Quell-Datei gibt.
    Source NC_CHAR Ersteller des Import-Filters, z. B. FEV Software and Testing Solutions GmbH (www.uniplot.com)
    Creator NC_CHAR Name des Import-Filters, dem Namen kann eine Versionsnummer folgen. Z. B. MDF 4.1
    Date NC_CHAR Datum als Zeichenkette, z. B. 14 Nov 2013
    Time NC_CHAR Zeit als Zeichenkette, z. B. 12:11:31,345

    Daneben kann die Datei noch weitere beliebige Attribute enthalten. Z. B. die Datei-Attribute, die in dem Quellformat vorhanden sind, wie Projekt-Nummer oder Bearbeiter.

    Der Formelinterpreter kann auf globale Attribute zugreifen. Beispiel: Der Luftdruck (barom) während der Messung könnte als Kanal angegeben werden, oder, wenn er während der Messung konstant war, als globales Attribut:

    barom = 1013.4
    

    Es sollte dann ein weiteres Attribut mit der Einheit geben:

    barom.units = "mbar"
    

    Ab UniPlot 2014 kann das Attribut auch als Zeichenkette mit Angabe der Einheit verwendet werden:

    barom = "1013.4 mbar"
    
  • Alle Kanäle sollten die folgenden Attribute besitzen:

    Attribut-Name Datentyp Bedeutung
    title NC_CHAR Titel für Anzeigezwecke in Diagrammen, kann gleich dem Kanalnamen sein.
    long_name NC_CHAR Titel für Anzeigezwecke in Diagrammen, kann gleich dem Kanalnamen sein, optional mit Einheit in eckigen Klammern: zlrsb_w\ETKC:1 [s]
    units NC_CHAR Physikalische Einheit, z. B. Nm.
    scale_factor NC_DOUBLE Im Allgemeinen der Wert 1.
    add_offset NC_DOUBLE Im Allgemeinen der Wert 0.

    Die folgenden Attribute sind optional.

    Attribut-Name Datentyp Bedeutung
    missing_value wie Kanal Der Datentyp des Attributs missing_value ist der Datentyp des Kanals. Nur erforderlich bei Integer-Kanälen, bzw. wenn bei NC_FLOAT und NC_DOUBLE der Wert 1e10 nicht verwendet werden soll.
    Type NC_CHAR Wert ist entweder „Time Channel“ oder „Data Channel“.
    datatype NC_CHAR Bei Datum/Zeitkanälen: Mögliche Werte: „date“, „time“, „datetime“
    C_format NC_CHAR Formatzeichenkette zur Anzeige der Daten im Browser, z. B. %.2lf für die Ausgabe von 2 Nachkommastellen oder %.6lf für 6 Nachkommastellen, siehe printf. Der Wert kann weggelassen werden, wenn ein sinnvoller Wert nicht bekannt.
    Description NC_CHAR Beschreibung des Kanals, z. B. Anzahl der Dynamikmessungen LSU oder leerer String.
    Comment NC_CHAR Kommentar zum Kanal.

    Daneben kann der Kanal beliebige weitere Attribute enthalten.

History

Version Beschreibung
1.4 (28.01.2019) zlib.dll zugefügt.
1.4 (01.11.2017) Korrekturen. Die beiden mitgelieferten Import-Libraries konnten nur mit R2015.x verwendet werden. Die nun mitgelieferten sind für R2016 und höher.
1.3 (09.01.2015) Beschreibung ergänzt.
1.3 (09.01.2015) Beschreibung ergänzt.
1.2 (21.11.2013) Beschreibung ergänzt.
1.1 (25.03.2011) Schreiben zugefügt.
1.0 (21.03.2011) Erste Version.

id-1961936