LibMseed.jl

LibMseed.jl wraps the libmseed library to read and write seismic data in miniSEED format
Author anowacki
Popularity
2 Stars
Updated Last
5 Months Ago
Started In
March 2022

LibMseed.jl

Build Status Code coverage

LibMseed.jl is a Julia wrapper around the libmseed library for reading and writing data in the miniSEED format.

Installation

Dependencies

  • Julia v1.6 or later.

Installation instructions

You can install LibMseed.jl from Julia's package manager like so:

julia> using Pkg; Pkg.add("LibMseed")

You do not need to separately install the libmseed library. Instead, LibMseed.jl installs its own version automatically.

Using the package

LibMseed exports several functions with common names, such as read_file and write_file. It is recommended to either explicitly import the functions you need, or always prepend names with LibMseed.

Reading data from disk

Use read_file function to read miniSEED data from disk. Here we assume you have a file called example.mseed in the current directory.

julia> import LibMseed

julia> ms = LibMseed.read_file("example.mseed")
MseedTraceList:
 2 traces:
  "FDSN:GB_CWF__B_H_Z": 2008-02-27T00:56:45.404999000 2008-02-27T00:57:45.384999000, 1 segments
  "FDSN:GB_CWF__H_H_Z": 2008-02-27T00:56:45.409999000 2008-02-27T00:57:45.399999000, 1 segments

Reading data from memory

Use the LibMseed.read_buffer function to read miniSEED data from memory. This data should be a Vector of UInt8s.

julia> data = read("example.mseed") # This may have been downloaded from the internet, for example
26624-element Vector{UInt8}
0x00

0x00

julia> ms = LibMseed.read_buffer(data)
MseedTraceList:
 2 traces:
  "FDSN:GB_CWF__B_H_Z": 2008-02-27T00:56:45.404999000 2008-02-27T00:57:45.384999000, 1 segments
  "FDSN:GB_CWF__H_H_Z": 2008-02-27T00:56:45.409999000 2008-02-27T00:57:45.399999000, 1 segments

Reading specific channels or time ranges

read_file and read_buffer support the startdate, enddate and channels keyword arguments.

startdate and enddate limit the time window read, but the underlying libmseed library does not ensure that the first sample of the traces returned is at the start date requested, so you may need to further trim traces:

julia> using Dates: DateTime

julia> file = joinpath(dirname(pathof(LibMseed)), "..", "test", "data", "Int32-oneseries-mixedlengths-mixedorder.mseed");

julia> ms = LibMseed.read_file(file)
MseedTraceList:
 1 trace:
  "FDSN:XX_TEST_00_L_H_Z": 2010-02-27T06:50:00.069539000 2010-02-27T07:55:51.069539000, 1 segments

julia> LibMseed.read_file(file; startdate=DateTime(2010, 2, 27, 7, 45))
MseedTraceList:
 1 trace:
  "FDSN:XX_TEST_00_L_H_Z": 2010-02-27T07:22:00.069539000 2010-02-27T07:55:51.069539000, 1 segments

channels is a string which is matched against the FDSN source ID of each channel to determine whether to read it. * indicates any number of any character (including zero), while ? indicates exactly one character of any kind. E.g., FDSN:GB_*Z matches all vertical channels in the GB network.

Accessing data

LibMseed.read_file returns a LibMseed.MseedTraceList, which is a structure holding an arbitrary number of traces (corresonding to individual channels), each of which may hold an arbitrary number of segments.

Access the traces within the MseedTraceList object via its traces property:

julia> ms.traces
2-element Vector{LibMseed.MseedTraceID}:
 LibMseed.MseedTraceID{Int32}("FDSN:GB_CWF__B_H_Z", NanosecondDateTime("2008-02-27T00:56:45.404999000"), NanosecondDateTime("2008-02-27T00:57:45.384999000"), LibMseed.MseedTraceSegment{Int32}[LibMseed.MseedTraceSegment{Int32}(NanosecondDateTime("2008-02-27T00:56:45.404999000"), NanosecondDateTime("2008-02-27T00:57:45.384999000"), 50.0, 3000, Int32[1466, 1466, 1453, 1449, 1449, 1443, 1441, 1443, 1444, 1439    -12421, -15146, 6993, 32994, 34813, 29718, 17484, 4468, 13498, 21614])])
 LibMseed.MseedTraceID{Int32}("FDSN:GB_CWF__H_H_Z", NanosecondDateTime("2008-02-27T00:56:45.409999000"), NanosecondDateTime("2008-02-27T00:57:45.399999000"), LibMseed.MseedTraceSegment{Int32}[LibMseed.MseedTraceSegment{Int32}(NanosecondDateTime("2008-02-27T00:56:45.409999000"), NanosecondDateTime("2008-02-27T00:57:45.399999000"), 100.0, 6000, Int32[1469, 1469, 1463, 1465, 1447, 1449, 1457, 1450, 1447, 1446    28750, 19408, 13748, 9836, -1323, 11130, 21097, 20900, 14103, 10817])])

Each trace is a LibMseed.MseedTraceID which has a single channel code. It may be divided up into several non-contiguous segments, which can be accessed by the MseedTraceID's segments property:

julia> ms.traces[1].segments
1-element Vector{LibMseed.MseedTraceSegment{Int32}}:
 LibMseed.MseedTraceSegment{Int32}(NanosecondDateTime("2008-02-27T00:56:45.404999000"), NanosecondDateTime("2008-02-27T00:57:45.384999000"), 50.0, 3000, Int32[1466, 1466, 1453, 1449, 1449, 1443, 1441, 1443, 1444, 1439    -12421, -15146, 6993, 32994, 34813, 29718, 17484, 4468, 13498, 21614])

Here, the channel GB.CWF..BHZ has only one segment. The raw data can be obtained by accessing that segment's data property:

julia> ms.traces[1].segments[1].data
3000-element Vector{Int32}:
   1466
   1466
      
  13498
  21614

Note that the data element type is a parameter of the channel's MseedTraceID object and each segment of a trace must have the same element type.

Channel naming of input data

Channel names are simply read from the miniSEED file or data and may or may not be in a standard SEED format. However, you can use the unexported LibMseed.channel_code_parts on a MseedTraceID or a string to try and split the channel's ID into network, station, location and channel as in traditional SEED conventions:

julia> LibMseed.channel_code_parts(ms.traces[1])
(net = "GB", sta = "CWF", loc = "", cha = "BHZ")

LibMseed.channel_code_parts returns a named tuple with the component parts. If a trace ID doesn't seem to correspond to the network-station-location-channel format, then the whole ID string is returned in the sta field of the named tuple and all other fields are set to "" (the empty string).

Writing data

To write a continuous set of evenly-spaced samples to disk in miniSEED format, use the LibMseed.write_file function.

Here we create some random data, set the necessary parameters (including the date of the first sample) and write it to a new file, "example2.mseed".

julia> using Dates: DateTime

julia> data = randn(1000);

julia> sampling_rate = 100; # Hz

julia> id = "FDSN:XX_STA_00_H_H_Z";

julia> starttime = DateTime(2000);

julia> LibMseed.write_file("example2.mseed", data, sampling_rate, starttime, id)
2

If we wanted to add a separate segment of data for this channel, or a different channel entirely, then we can call write_file again but use the append=true keyword argument:

julia> data2 = randn(100)

julia> starttime2 = DateTime(2000, 1, 1, 12);

julia> LibMseed.write_file("example2.mseed", data2, sampling_rate, starttime2, id; append=true)
1

julia> ms2 = LibMseed.read_file("example2.mseed")
MseedTraceList:
 1 trace:
  "FDSN:XX_STA_00_H_H_Z": 2000-01-01T00:00:00.000000000 2000-01-01T12:00:00.990000000, 2 segments

julia> segs = only(ms2.traces).segments;

julia> getproperty.(segs, [:starttime :endtime]) # One row per segment, start and end time
2×2 Matrix{LibMseed.NanosecondDateTime}:
 2000-01-01T00:00:00.000000000    2000-01-01T00:00:09.990000000
 2000-01-01T12:00:00.000000000     2000-01-01T12:00:00.990000000

Note on channel naming

Note that the libmseed library requires that the trace ID has the form shown above, i.e. "FDSN:NET_STA_LOC_BAND_SOURCE_POSITION", otherwise an error is thrown.

Time resolution

The time of the first sample (the starttime in the example above, or the property starttime of a segment) is stored to nanosecond precision in the miniSEED file. The standard library Dates module cannot handle nanosecond resolution Dates.DateTimes, and so time is implemented in LibMseed.jl via the LibMseed.NanosecondDateTime type.

You can convert a NanosecondDateTime to the nearest Dates.DateTime (precise to the millisecond) with LibMseed.nearest_datetime. Use Dates.Time to obtain the time of day to full nanosecond resolution.

julia> t = ms.traces[1].segments[1].starttime
2008-02-27T00:56:45.404999000

julia> LibMseed.nearest_datetime(t)
2008-02-27T00:56:45.405

julia> using Dates: Time

julia> Time(t)
00:56:45.404999

More help

Each of the functions described above is further documented via its docstring.

Contributing

Pull requests to add functionality and fix bugs are welcome. To report a bug or feature request in the software, please open an issue.