|
libsdfio 1.0.0
Reader and writer for ISO 25178-71 SDF surface data files in C
|
An SDF file contains the heights of a profile or of an areal surface measurement on a rectangular grid. It is used as a software measurement standard, a set of reference data with which the software of surface texture measuring instruments is verified, and as an interchange format for topography data. A file consists of three records: the header, the data area and the trailer. The file name extension is .sdf. A file has a 7-bit ASCII or a binary format. Both contain the same information. A reader or a writer supports a format after it is added with sdfio_reader_add_binary(), sdfio_reader_add_ascii() or one of the two functions of the writer. The file starts with a magic of 8 characters, which the standard calls the version number. Its first character is a for the ASCII and b for the binary format, and the rest names the version: aISO-1.0, aISO-2.0, bISO-1.0 and bISO-2.0. The library calls the version a revision, ISO-1.0 and ISO-2.0. ISO-1.0 is the standardized form of BCR-1.0, the format that preceded the standard, described by K. J. Stout et al., The Development of Methods for the Characterisation of Roughness in Three Dimensions, EUR 15178 EN, 1993. ISO-1.0 allows the data types int16_t, int32_t and double for the data area, stores the numbers of columns and rows of a binary file as uint16_t and has dates without a time zone. ISO-2.0 adds int8_t and float for the data area, stores the numbers as uint32_t, has dates in UTC and a trailer in the form of the header.
The values of the data area have one of these data types, which the header selects with its code.
| Data Type | Code | Size | ISO-1.0 | ISO-2.0 | Value of a Point That Is Not Measured |
|---|---|---|---|---|---|
| float (binary32) | 3 | 4 bytes | no | yes | -FLT_MAX (SDFIO_BAD_FLOAT) |
| int8_t | 4 | 1 byte | no | yes | INT8_MIN (SDFIO_BAD_INT8) |
| int16_t | 5 | 2 bytes | yes | yes | INT16_MIN (SDFIO_BAD_INT16) |
| int32_t | 6 | 4 bytes | yes | yes | INT32_MIN (SDFIO_BAD_INT32) |
| double (binary64) | 7 | 8 bytes | yes | yes | -DBL_MAX (SDFIO_BAD_DOUBLE) |
A point that is not measured, or is spurious, is labelled with the minimum value of the data type, which the standard does not permit for any valid coded value. An ASCII file writes the string BAD instead.
| Field Name | Binary Type | Meaning |
|---|---|---|
ManufacID | char[10] | Manufacturer-specific identifier. |
CreateDate | char[12] | When the data was generated. |
ModDate | char[12] | When the file was last modified. |
NumPoints | uint16_t or uint32_t | Number of columns M, the points along x. |
NumProfiles | uint16_t or uint32_t | Number of rows N, the points along y. 1 for a profile. |
Xscale | double | Sampling interval along x in metres. |
Yscale | double | Sampling interval along y in metres, meaningless for a profile. |
Zscale | double | Positive factor that turns a coded value into metres. |
Zresolution | double | Quantization step of the instrument in metres, or a negative number if it is unknown. |
Compression | uint8_t | 0, no compression. |
DataType | uint8_t | Code of the data type of the values in the data area, see Data Types. |
CheckType | uint8_t | 0, no checksum. |
The names NumPoints and NumProfiles stem from tactile instruments that measure profiles: NumPoints is the number of points of a profile and NumProfiles the number of profiles. In a binary file the fields follow the magic in the order of the table. In an ASCII file the magic is the first line, and the other fields can appear in any order.
A date is a string of 12 digits, DDMMYYYYhhmm: day, month, year, hour and minute, with leading zeros. The date of an ISO-2.0 file is UTC. The dates of an ISO-1.0 file are the local clock reading of the writer, and the file does not record the time zone.
The library handles every date as a UTC time: the functions of the header take and return a time_t in UTC. A date of an ISO-2.0 file needs no conversion. A date of an ISO-1.0 file is converted by a time zone, which turns the clock reading into UTC when a reader opens the file, and the UTC time back into a clock reading when a writer writes the header. The time zone is set with sdfio_reader_set_time_zone() or sdfio_writer_set_time_zone(). sdfio_system_time_zone() is the time zone of the machine, and an own conversion is passed as an sdfio_time_zone. Without a time zone the library takes the clock reading as UTC. The UTC time is correct only if the time zone is the one in which the file was written.
The data area contains the matrix W of coded values with N rows and M columns, stored row by row in the order of y. The coded value w(i,j) of the row i and the column j, counted from 1, belongs to the point
in a right-handed coordinate system: x grows with the column, y with the row, and z points to a viewer who looks from above. The first row has y = 0, so it is the bottom row of a picture with x to the right. sdfio_read_data(), sdfio_read_heights() and the writing functions use this order of rows. Image libraries count the rows from the top and show the surface upside down if it is not flipped.
A coded value is not a length. Multiplied by the z-scale it is the height of the point in metres. For an integer type the z-scale sets the value of one step and so trades range against resolution: heights between -80 and +80 micrometres in int16_t values need a z-scale of about 2.4e-9 metres. A floating-point type usually stores metres directly with the z-scale 1, but any z-scale is valid.
The library offers three levels of access; use the highest one that suffices. sdfio_read_data() and sdfio_write_data() transfer the coded values row by row. sdfio_decode_heights() and sdfio_encode_heights() convert rows between coded values and heights in metres and need only a header. sdfio_read_heights() and sdfio_write_heights() transfer the whole matrix as heights. Encoding rounds an integer to the nearest value. A height that does not fit the data type with the z-scale is an error (SDFIO_ERR_RANGE), and so is a height that would be written as the bad value (SDFIO_ERR_BAD_VALUE), which a reader would take for a point that is not measured. sdfio_suggest_z_scale_for_range() and sdfio_suggest_z_scale() find the smallest z-scale that fits the heights, and sdfio_write_heights() uses it when the header has SDFIO_AUTO_Z_SCALE.
A point that is not measured has the bad value of its data type as coded value. In heights the library uses NaN for such a point. The functions that read or convert heights deliver NaN for such a point, and the functions that write or convert heights take NaN and infinity as such a point. Data that marks these points with a value such as -9999 has to be replaced with NaN before writing, and NaN with the marker after reading.
The trailer contains historical information about the measurement, such as the name of the operator, the measurement conditions or the processing that was applied. It is optional, has a variable length, and extends to the end of the file. ISO-1.0 does not define its content, and the trailer of an ISO-2.0 file has the form of the header, a line Name = Value for each field. The standard asks for text in 7-bit ASCII, which the library does not check: it reads and writes the bytes as they are. The lines of a trailer end with a carriage return and a line feed in the file. The library returns every line end as a line feed, and writes every line end of a trailer as a carriage return and a line feed. sdfio_trailer_find_field() and sdfio_trailer_next_field() find the fields in the text that sdfio_read_trailer() returns, and give the name and the value as offsets into that text.
The records follow each other without a separator. Numbers are stored with the least significant byte first, and the library converts the byte order. A manufacturer-specific identifier of fewer than 10 characters is filled with spaces.
The file consists of 7-bit ASCII characters in lines that end with a carriage return and a line feed. The first line is the magic, followed by a line Name = value for each field of the header, whose names do not depend on the case. Each record ends with a line that consists of the character *, and the last such line ends the file. The values of the data area are separated by white space of any length. A float is written with 7 significant digits and a two-digit exponent, as in 1.500000e+00, a double with 15 digits and a three-digit exponent, as in 1.50000000000000e+000, and an integer in decimal digits.