netCDF-SDK

This document describes how to read and write NC and NC2 files with C.

Contents

The following files must be installed with your application. The files can be copied from a current UniPlot installation (R2018 or later). A 32bit and a 64 bit version is available. The file license.txt can be found in the UniPlot\samples\SDK\netCDF directory.

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

Other files from the UniPlot\samples\SDK\netCDF directory:

ncread.c            - A simple example how to read data.
ncwrite.c           - A simple example how to write data.
win32\rs_updf_nc.lib      - The 32-Bit Import-Library.
x64\rs_updf_nc.lib  - The 64-Bit Import-Library.
up-nc.h             - The Header file.
map-data.nc2        - Simple example file.

Some Notes

  • The UniPlot-netCDF-SDK uses the netCDF 2.4 API.

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

  • The netCDF data files used with UniPlot can have three different formats:

    1. The original netCDF format as described in the link above. File size is limited to 2 GBytes. The first three characters in the file are “CDF”.

    2. A modified form of the original netCDF format with Intel byte order (Little Endian). These files cannot be read with the standard netCDF library. The file size is limited to 2 GBytes. The first three characters in the file are “CDH”.

    3. A UniPlot format that uses only the API of the netCDF library. The format supports compression and fast delete/add channel and attributes. The first three characters in the file are “UPD”. These files are similar to a file system. The data is saved in sectors.

      The file size is limited to 1 TB. Each channel can contain up to 2^31-1 data points.

  • If you use a Microsoft C-Compiler you can compile the examples with:

    cl ncread.c x64\rs_updf_nc.lib
    

    The path for your C-Compiler must be set.

    Start the program with:

    ncread map-data.nc2
    

    The example prints the first 10 data points of each channel. More test files can be created using UniPlot.

  • Instead of using the Import-Library rs_updf_nc.lib, you can load the functions dynamically using LoadLibrary, (LoadLibraryEx, GetModuleHandle) and GetProcAddress.

  • For some data file formats, for example INCA-MDF, UniPlot saves the data in the netCDF file with a delay. The file contains the complete structure with all attributes and channels. The channel data is saved in the netCDF file, when the channel data is accessed.

    To check if the data is already saved in the netCDF file read the value of the channel attribute _nc_dl_loaded. If the attribute has the value 1 the data is valid.

Examples

 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 Attributes

    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");

    // Dimensions
    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);

    // Write the channel data

    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;
}

Writing a string channel

 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");

    // Number of Points 10,000
    n1 = ncdimdef(ncid, "n", 10000);

    // Legth of the longest string, e. g. 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, "Hello");

    s = "This is a long string";
    start[0] = 10;
    start[1] = 0;
    count[0] = 1;
    count[1] = MIN(32, strlen(s));
    ncvarput(ncid, varid, start, count, s);

    ncclose(ncid);
}

Read file:

  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("%f\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("%f\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("%f\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);
}

Conventions

  • Valid characters for attribute and channel names are: _[a-z][A-Z][0-9], A name should not start with an alphanumeric character. The name length is limited to 128 characters. The following 10 special characters are also allowed in names: . - + $ # ~ ! ^ & %.

  • Attribute and channel names should not start with an underscore _ or the characters nc_`. These names are reserved for internal purposes (e. g. for the attributes nc_min, nc_monotone, _FillValue, _nc_dl_loadinfo, etc.).

  • Channel names must be unique. The number of channels is limited to 20,000 per file.

  • Missing Values: For NC_DOUBLE- and NC_FLOAT channels the value 1e10 should be used as a missing value. For integer channels the attriubte missing_value should be added to the channel.

  • For time channels the channel data type NC_DOUBLE` (8 Bytes) should be used.

  • Time/Date channels should be saved in the Microsoft Excel Format with the datatype NC_DOUBLE:

    uniplot_time = unix_time / 86400 + 25569
    
    The Unix time are the seconds since 1.1.1970 00:00:00.
    The Excel time are the days since December 31, 1899.
    
    86400: seconds in a day (24*60*60)
    25569: days beween 1899-12-31 and 1970-01-01
    

    The attribute datatype should be saved with the Time/Date channel with one of the following values: “date”, “time” or “datetime”.

  • A string channel is a matrix of type NC_CHAR. The second dimention is the number of characters of the longest string. All other stings will be padded with NULL, see example below.

  • The following attributes should be saved in the file as global attributes:

    Attribute-Name Datatype Description
    Origin NC_CHAR Name of the original data file, e.g. c:\test-data\test.mdf4. Can be empty.
    Source NC_CHAR Create of the import filter, e. g. FEV Software and Testing Solutions GmbH (www.uniplot.com)
    Creator NC_CHAR Name of the import filter followed by a version number, e. g. MDF 4.1
    Date NC_CHAR Date as a string, e. g. 14 Nov 2013
    Time NC_CHAR Time as a string, e. g. 12:11:31,345

    In addition, the file can also contain other arbitrary attributes. For example, the project number, engine information,etc.

    The formula interpreter can access global attributes. example: The air pressure (barom) during the measurement could be specified as a channel or, if it was constant during the measurement, as a global attribute

    barom = 1013.4
    

    In this case another addtribute should define the unit:

    barom.units = "mbar"
    

    From UniPlot 2014, the attribute can be defined as following:

    barom = "1013.4 mbar"
    
  • Channels should have the following attributes:

    Attribute-Name Datatype Description
    title NC_CHAR Channel title.
    long_name NC_CHAR A long descriptive name. This could be used for labeling plots, for example.: zlrsb_w\ETKC:1 [s]
    units NC_CHAR A character string that specifies the units used for the variable’s data, e. g. Nm.
    scale_factor NC_DOUBLE If present for a variable, the data are to be multiplied by this factor after the data are read by the application that accesses the data. Normally 1.
    add_offset NC_DOUBLE If present for a variable, this number is to be added to the data after it is read by the application that accesses the data. Normally the value 0.

    The following attributes are optional.

    Attribute-Name Datatype Description
    missing_value As channel The data type of the attribute missing_value is the data type of the channel. Only required for integer channels, or if the data type is NC_FLOAT or NC_DOUBLE and the value 1e10 is not intended to be used.
    Type NC_CHAR Value is “Time Channel” or “Data Channel”.
    datatype NC_CHAR For Time/Date channel, values: “date” or “time” or “datetime”
    C_format NC_CHAR Format string to display the values in the data browser, e. g. %.2lf for two decimal places or %.6lf for 6 decimal places. see printf.
    Description NC_CHAR Description string.
    Comment NC_CHAR Comment string.

    In addition, the channel can also contain other attributes.

History

Version Description
1.4 (28.01.2019) zlib.dll added to the list of DLLs.
1.4 (01.11.2017) Correction. The two import libraries could only be used with R2015.x. The new libraries are for R2016 and higher.
1.3 (09 Jan 2015) Description modified.
1.2 (21 Nov 2013) Description modified.
1.1 (25 Mar 2011) Writing added.
1.0 (21 Mar 2011) First version.

Contact

If you have any questions please contact us:

id-1961936