.. highlightlang:: us .. index:: netCDF-SDK .. _netCDF-SDK: 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 :ref:`tools-more-options` 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. .. _netCDF-SDK-example: Beispiele --------- .. code-block:: c :linenos: /* compile: cl ncwrite.c rs_updf_nc.lib */ #include #include #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 .. code-block:: c :linenos: // compile: cl test_nc_string.c rs_updf_nc.lib #include #include #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: .. code-block:: c :linenos: /* 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 #include #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); } .. _netCDF-SDK-conventions: 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: .. list-table:: :header-rows: 1 * - 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: .. list-table:: :header-rows: 1 * - 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. .. list-table:: :header-rows: 1 * - 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 :ref:`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. .. _netCDF-SDK-seealso: Siehe auch ---------- .. list-table:: :header-rows: 0 * - :ref:`overview-netcdf-files` * - :ref:`ncdump_exe` * - :ref:`ncgen_exe` * - :ref:`overview-date-and-time` History ------- .. list-table:: :header-rows: 1 * - 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. :sub:`id-1961936`