LibMseed.jl is a Julia wrapper around the libmseed library for reading and writing data in the miniSEED format.
- Julia v1.6 or later.
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.
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.
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 segmentsUse 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 segmentsread_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 segmentschannels 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.
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
  21614Note 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 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).
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)
2If 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.990000000Note 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.
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.404999Each of the functions described above is further documented via its docstring.
Pull requests to add functionality and fix bugs are welcome. To report a bug or feature request in the software, please open an issue.