| Title: | Ground Motion Signal Processing |
|---|---|
| Description: | Implements short-time Fourier transform (STFT) based processing of strong-motion time series: time-grid regularisation, STFT-window and anti-alias-resampling strategy selection, edge tapering, and frequency-domain integration and differentiation, mapping a single input (acceleration, velocity, or displacement) to a consistent triplet under a chosen analysis bandwidth. Also provides intrinsic-mode-function decomposition via empirical mode decomposition (EMD), ensemble EMD (EEMD), and variational mode decomposition (VMD) with optional band-rule filtering; elastic single-degree-of-freedom (SDOF) response spectra (pseudo-spectral acceleration, velocity, and displacement) by exact state-space integration; intensity measures including peak, root-mean-square (RMS), Arias intensity, significant-duration, cumulative absolute velocity, mean period, and the derived indices earthquake destructiveness potential (EPI) and power-of-input (PDI); and D50 and D100 horizontal response spectra. Methods: Huang et al. (1998) <doi:10.1098/rspa.1998.0193>, Wu and Huang (2009) <doi:10.1142/S1793536909000047>, Dragomiretskiy and Zosso (2014) <doi:10.1109/TSP.2013.2288675>, Boore (2010) <doi:10.1785/0120090179>. An optional indexing layer parses provider files in formats including 'PEER' 'NGA-West2' 'AT2', 'CESMD' 'V2'/'V2c', 'NWZ' 'V2A', Geological Survey of Canada 'TR', 'IGP'/'UCR' 'AC' variants, and generic two-column ASCII text, normalises components, writes per-record CSV (comma-separated values) and JSON (JavaScript Object Notation) pairs, and assembles a master record table. |
| Authors: | Alejandro Verri Kozlowski [aut, cre, cph] (ORCID: <https://orcid.org/0000-0002-8535-1170>) |
| Maintainer: | Alejandro Verri Kozlowski <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 0.4.6 |
| Built: | 2026-06-19 13:56:23 UTC |
| Source: | https://github.com/cran/gmsp |
Pads shorter OCIDs with trailing zeros (align = "max") or truncates
longer OCIDs (align = "min") so all components share the same NP.
Operates on a classified LONG table with columns t, OCID, s, and DIR.
alignComponents(DT, align = "max")alignComponents(DT, align = "max")
DT |
LONG |
align |
|
Named list with two elements:
DT : aligned LONG data.table with the same columns as the
input.
NP : integer, final number of samples per component after
alignment (the same for every OCID).
When every OCID already shares the same NP the function returns
without padding or truncation; the input is passed through as
DT unchanged.
x <- data.table::rbindlist(list( data.table::data.table(t = c(0, 0.01, 0.02), OCID = "H1", DIR = "H1", s = c(1, 2, 3)), data.table::data.table(t = c(0, 0.01), OCID = "H2", DIR = "H2", s = c(1, 2)), data.table::data.table(t = c(0, 0.01, 0.02), OCID = "UP", DIR = "UP", s = c(0, 1, 0)) )) aligned <- alignComponents(x, align = "max") aligned$NPx <- data.table::rbindlist(list( data.table::data.table(t = c(0, 0.01, 0.02), OCID = "H1", DIR = "H1", s = c(1, 2, 3)), data.table::data.table(t = c(0, 0.01), OCID = "H2", DIR = "H2", s = c(1, 2)), data.table::data.table(t = c(0, 0.01, 0.02), OCID = "UP", DIR = "UP", s = c(0, 1, 0)) )) aligned <- alignComponents(x, align = "max") aligned$NP
raw.owner/ to raw.owner.tar.gz and delete the directory.Saves disk space after the parser has succeeded. Returns silently if
nothing to do (no raw.owner/ directory) or if the archive already
exists. Only deletes raw.owner/ after the tar archive is verified
(re-readable). On any failure, leaves both intact.
archiveRawOwner(path)archiveRawOwner(path)
path |
Absolute path to the station folder containing |
Logical, TRUE if archive newly written, FALSE if no-op,
NA on failure.
station <- file.path(tempdir(), "gmsp-archive-example") unlink(station, recursive = TRUE) dir.create(file.path(station, "raw.owner"), recursive = TRUE) writeLines("provider bytes", file.path(station, "raw.owner", "record.txt")) archiveRawOwner(station) file.exists(file.path(station, "raw.owner.tar.gz"))station <- file.path(tempdir(), "gmsp-archive-example") unlink(station, recursive = TRUE) dir.create(file.path(station, "raw.owner"), recursive = TRUE) writeLines("provider bytes", file.path(station, "raw.owner", "record.txt")) archiveRawOwner(station) file.exists(file.path(station, "raw.owner.tar.gz"))
End-to-end workflow that takes acceleration time histories and produces a consistent set of acceleration, velocity, and displacement time series. It optionally regularizes sampling, converts units (for raw data), selects optimal STFT parameters and resampling strategy, applies robust edge tapering, performs spectral-domain integration, and provides post-tapering/optional trimming.
The function is designed for seismic/structural records but is agnostic to
the physical origin provided Fmax reflects the analysis band of interest.
AT2TS( .x, units.source, time = "t", Fmax = 16, kNyq = 3.125, resample = TRUE, units.target = "mm", NW = 128, OVLP = 75, flatZeros = FALSE, Astop0 = 1e-04, Apass0 = 0.001, AstopLP = 0.001, ApassLP = 0.98, trimZeros = FALSE, detrend = FALSE, regularize = FALSE, output = "TSL", verbose = FALSE, audit = TRUE, isRaw = TRUE )AT2TS( .x, units.source, time = "t", Fmax = 16, kNyq = 3.125, resample = TRUE, units.target = "mm", NW = 128, OVLP = 75, flatZeros = FALSE, Astop0 = 1e-04, Apass0 = 0.001, AstopLP = 0.001, ApassLP = 0.98, trimZeros = FALSE, detrend = FALSE, regularize = FALSE, output = "TSL", verbose = FALSE, audit = TRUE, isRaw = TRUE )
.x |
data.table. Input acceleration records with a time column and one or more signal columns (e.g., H1, H2, V). |
units.source |
character. Source units for input acceleration when
|
time |
character. Name of the time column in the input (default |
Fmax |
numeric. Maximum frequency of interest (Hz). Used to set STFT
strategy and low-pass regularization in integration. Default: |
kNyq |
numeric. Target Nyquist multiplier ( |
resample |
logical. Kept for compatibility; the actual decision is made by
the internal STFT strategy based on |
units.target |
character. Output target units for acceleration records. Default: "mm". |
NW |
integer. Nominal STFT window length (samples); may be adjusted by the
strategy. Default: |
OVLP |
numeric. Window overlap percent. Default: |
flatZeros |
logical. Apply edge tapering to suppress low-level pre/post
segments. If |
Astop0 |
numeric. Normalized stop threshold |
Apass0 |
numeric. Normalized pass threshold |
AstopLP |
numeric. Stopband attenuation for anti-alias LP (resampling).
Default: |
ApassLP |
numeric. Passband for anti-alias LP (resampling). Default: |
trimZeros |
logical. If |
detrend |
logical. Remove mean before/after each main stage. Default: |
regularize |
logical. Force time regularization of input if needed.
Default: |
output |
character. Short-circuit outputs (default: "TSL"): "ATo": early wide-frame after units; "AT"/"VT"/"DT": final wide; "TSW": combined wide; "TSL": long table. |
verbose |
logical. Print diagnostic logs. Default: |
audit |
logical. If |
isRaw |
logical. If |
Returns the requested object based on output (no other
element is returned alongside it):
"ATo": wide table with ts (time starting at 0), Units and
channels, before any tapering or integration.
"AT" / "VT" / "DT": wide table with the channels only (no
ts column).
"TSW": wide table with columns ts, AT.<OCID>, VT.<OCID>,
DT.<OCID>.
"TSL" (default): long table with columns t, s, ID
(one of "AT", "VT", "DT"), and OCID.
Sampling-related scalars (Fs, dt, df, NP, ...) are computed
internally during processing but are not part of the return value;
recover them from the output via 1 / diff(ts)[1] and nrow(.).
t <- seq(0, 2, by = 0.02) x <- data.table::data.table( t = t, H1 = sin(2 * pi * t), H2 = 0.5 * cos(2 * pi * t), UP = 0.25 * sin(4 * pi * t) ) tsl <- AT2TS(x, units.source = "mm", Fmax = 4, NW = 16, audit = FALSE, isRaw = FALSE) head(tsl)t <- seq(0, 2, by = 0.02) x <- data.table::data.table( t = t, H1 = sin(2 * pi * t), H2 = 0.5 * cos(2 * pi * t), UP = 0.25 * sin(4 * pi * t) ) tsl <- AT2TS(x, units.source = "mm", Fmax = 4, NW = 16, audit = FALSE, isRaw = FALSE) head(tsl)
Flags each row with the FIRST applicable reason from a fixed precedence: lat/lon NA, lat/lon out of range, depth negative, Repi above outlier threshold, Rhyp < Repi (geometric impossibility). Rows with no issue are dropped from the output.
auditDistances(DT, repiOutlier = 5000)auditDistances(DT, repiOutlier = 5000)
DT |
master |
repiOutlier |
threshold in km above which |
Does NOT read record.json or provider flatfiles. Comparison against
raw/flatfile distances is deferred to v2.
data.table of flagged rows with column Reason.
x <- data.table::data.table( EventLatitude = c(0, 95), EventLongitude = c(0, 0), StationLatitude = c(0.1, 0.1), StationLongitude = c(0.1, 0.1), EventDepth = c(10, 10), Repi = c(15, 20), Rhyp = c(18, 25) ) auditDistances(x)x <- data.table::data.table( EventLatitude = c(0, 95), EventLongitude = c(0, 0), StationLatitude = c(0.1, 0.1), StationLongitude = c(0.1, 0.1), EventDepth = c(10, 10), Repi = c(15, 20), Rhyp = c(18, 25) ) auditDistances(x)
parseRecord on every record of an owner.Iterates the master subset for owner, calls parseRecord per
unique (EventID, StationID), catches errors, and returns a status
table.
auditParsers(.x, owner, path)auditParsers(.x, owner, path)
.x |
master |
owner |
one OwnerID string. |
path |
Absolute path to the records root passed through to
|
data.table(OwnerID, EventID, StationID, status, reason).
root <- file.path(tempdir(), "gmsp-auditparsers-example") unlink(root, recursive = TRUE) raw <- file.path(root, "ESM", "E1", "S1", "raw.owner") dir.create(raw, recursive = TRUE) writeLines(c("0 1", "0.01 2", "0.02 3"), file.path(raw, "N_acc.txt")) writeLines(c("0 2", "0.01 3", "0.02 4"), file.path(raw, "E_acc.txt")) writeLines(c("0 0", "0.01 1", "0.02 0"), file.path(raw, "Z_acc.txt")) rows <- data.table::data.table( OwnerID = "ESM", EventID = "E1", StationID = "S1", FileID = c("N_acc.txt", "E_acc.txt", "Z_acc.txt") ) auditParsers(rows, owner = "ESM", path = root)root <- file.path(tempdir(), "gmsp-auditparsers-example") unlink(root, recursive = TRUE) raw <- file.path(root, "ESM", "E1", "S1", "raw.owner") dir.create(raw, recursive = TRUE) writeLines(c("0 1", "0.01 2", "0.02 3"), file.path(raw, "N_acc.txt")) writeLines(c("0 2", "0.01 3", "0.02 4"), file.path(raw, "E_acc.txt")) writeLines(c("0 0", "0.01 1", "0.02 0"), file.path(raw, "Z_acc.txt")) rows <- data.table::data.table( OwnerID = "ESM", EventID = "E1", StationID = "S1", FileID = c("N_acc.txt", "E_acc.txt", "Z_acc.txt") ) auditParsers(rows, owner = "ESM", path = root)
Flags rows with the FIRST applicable reason: StationVs30 NA, below
low cutoff, or above high cutoff. Coord checks live in auditDistances.
auditSite(DT, vs30Low = 50, vs30High = 3000)auditSite(DT, vs30Low = 50, vs30High = 3000)
DT |
master |
vs30Low |
lower physical cutoff for |
vs30High |
upper physical cutoff (m/s). |
data.table of flagged rows with column Reason.
x <- data.table::data.table(StationVs30 = c(30, 760, NA)) auditSite(x)x <- data.table::data.table(StationVs30 = c(30, 760, NA)) auditSite(x)
Two-stage hydration, per owner:
buildMaster(path, owners = NULL)buildMaster(path, owners = NULL)
path |
Absolute path to the index root holding the per-owner CSVs. Required – no default. |
owners |
character vector of OwnerIDs. |
Stage 1D (one row per record): inner-joins
RawRecordTable.<O>.csv with EventTable.<O>.csv on EventID
and StationTable.<O>.csv on StationID. Computes record-level
scalars: Repi (haversine, km) and Rhyp
(sqrt(Repi^2 + EventDepth^2), km). Future record-level
enrichments (geotechnical depth proxies, etc.) belong in this
stage.
Stage 3D (three rows per record, one per DIR): inner-joins
the Stage-1D output with RawIntensityTable.<O>.csv on
RecordID. Brings per-direction intensities (PGA, AI, ARMS,
...), plus per-direction NP, Fs, dt.
Event-level scalars (mag, depth, lat/lon, time, mechanism, region)
are consolidated by source precedence: *.owner > *.USGS >
*.ISC (and *.STREC for mechanism/region). StationVs30 is
already consolidated upstream in StationTable.
Required input files per owner under path:
RawRecordTable.<OwnerID>.csv (drives the owner list),
EventTable.<OwnerID>.csv, StationTable.<OwnerID>.csv,
RawIntensityTable.<OwnerID>.csv. The function errors if any of
the joined CSVs is missing; the canonical way to obtain them is to
run buildRawRecordTable(), buildRawIntensityTable(), and the
provider-specific event / station table builders.
data.table keyed at the (RecordID, DIR) level.
index <- file.path(tempdir(), "gmsp-master-example") unlink(index, recursive = TRUE) dir.create(index) data.table::fwrite( data.table::data.table(RecordID = "R1", EventID = "E1", StationID = "S1", OwnerID = "AAA", NP = 101, Fs = 100, pad = 0), file.path(index, "RawRecordTable.AAA.csv") ) data.table::fwrite( data.table::data.table( EventID = "E1", EventMagnitude.owner = 5.5, EventMagnitude.USGS = 5.4, EventMagnitude.ISC = 5.3, EventMagnitudeType.owner = "Mw", EventMagnitudeType.USGS = "Mw", EventMagnitudeType.ISC = "Mw", EventDepth.owner = 10, EventDepth.USGS = 11, EventDepth.ISC = 12, EventLatitude.owner = 0, EventLatitude.USGS = 0, EventLatitude.ISC = 0, EventLongitude.owner = 0, EventLongitude.USGS = 0, EventLongitude.ISC = 0, EventTimeUTC.owner = "2026-01-01T00:00:00Z", EventTimeUTC.USGS = "2026-01-01T00:00:00Z", EventTimeUTC.ISC = "2026-01-01T00:00:00Z", EventMechanism.owner = "SS", EventMechanism.STREC = "SS", EventTectonicRegion.owner = "active", EventTectonicRegion.STREC = "active" ), file.path(index, "EventTable.AAA.csv") ) data.table::fwrite( data.table::data.table(StationID = "S1", StationLatitude = 0.1, StationLongitude = 0.1, StationVs30 = 760), file.path(index, "StationTable.AAA.csv") ) data.table::fwrite( data.table::data.table(RecordID = "R1", DIR = "H1", OCID = "H1", PGA = 1), file.path(index, "RawIntensityTable.AAA.csv") ) buildMaster(index, owners = "AAA")index <- file.path(tempdir(), "gmsp-master-example") unlink(index, recursive = TRUE) dir.create(index) data.table::fwrite( data.table::data.table(RecordID = "R1", EventID = "E1", StationID = "S1", OwnerID = "AAA", NP = 101, Fs = 100, pad = 0), file.path(index, "RawRecordTable.AAA.csv") ) data.table::fwrite( data.table::data.table( EventID = "E1", EventMagnitude.owner = 5.5, EventMagnitude.USGS = 5.4, EventMagnitude.ISC = 5.3, EventMagnitudeType.owner = "Mw", EventMagnitudeType.USGS = "Mw", EventMagnitudeType.ISC = "Mw", EventDepth.owner = 10, EventDepth.USGS = 11, EventDepth.ISC = 12, EventLatitude.owner = 0, EventLatitude.USGS = 0, EventLatitude.ISC = 0, EventLongitude.owner = 0, EventLongitude.USGS = 0, EventLongitude.ISC = 0, EventTimeUTC.owner = "2026-01-01T00:00:00Z", EventTimeUTC.USGS = "2026-01-01T00:00:00Z", EventTimeUTC.ISC = "2026-01-01T00:00:00Z", EventMechanism.owner = "SS", EventMechanism.STREC = "SS", EventTectonicRegion.owner = "active", EventTectonicRegion.STREC = "active" ), file.path(index, "EventTable.AAA.csv") ) data.table::fwrite( data.table::data.table(StationID = "S1", StationLatitude = 0.1, StationLongitude = 0.1, StationVs30 = 760), file.path(index, "StationTable.AAA.csv") ) data.table::fwrite( data.table::data.table(RecordID = "R1", DIR = "H1", OCID = "H1", PGA = 1), file.path(index, "RawIntensityTable.AAA.csv") ) buildMaster(index, owners = "AAA")
For each station, reads raw.owner/record.json and emits one row
per provider file (one per ComponentID x FileID). Handles both states:
raw.owner/record.json present on disk (not yet archived).
raw.owner.tar.gz archive (extracts record.json via streaming
tar -xzOf to avoid touching disk).
buildRawFileTable(path.records, path.index, owners = NULL)buildRawFileTable(path.records, path.index, owners = NULL)
path.records |
Absolute path to the records root. Required – no default. |
path.index |
Absolute path to the index root where per-owner CSVs are written. Required – no default. |
owners |
Character vector of |
Schema:
OwnerID, EventID, StationID, ComponentID, FileID, NP, dt, Fs, Units, HP, LP, isArray
PGA is intentionally not emitted here. Pre-parse PGAs from
record.json are in heterogeneous provider units; canonical
post-parse PGAs (in mm/s^2) live in RawIntensityTable.<Owner>.csv.
Missing provider fields in the canonical schema are emitted as typed
NA columns instead of changing the output schema.
isArray = nComponentID > 3 (heuristic per legacy convention).
Invisibly, the per-owner row counts.
root <- file.path(tempdir(), "gmsp-raw-file-example") index <- file.path(tempdir(), "gmsp-raw-file-index") unlink(c(root, index), recursive = TRUE) dir.create(file.path(root, "AAA", "E1", "S1", "raw.owner"), recursive = TRUE) dir.create(index) record <- list( Event = list(EventID = "E1"), Station = list(StationID = "S1"), Record = list( list(ComponentID = "H1", FileID = "H1.txt", NP = 4, dt = 0.01, Fs = 100, Units = "cm", HP = NA, LP = NA), list(ComponentID = "H2", FileID = "H2.txt", NP = 4, dt = 0.01, Fs = 100, Units = "cm", HP = NA, LP = NA), list(ComponentID = "UP", FileID = "UP.txt", NP = 4, dt = 0.01, Fs = 100, Units = "cm", HP = NA, LP = NA) ) ) jsonlite::write_json( record, file.path(root, "AAA", "E1", "S1", "raw.owner", "record.json"), auto_unbox = TRUE ) suppressMessages(buildRawFileTable(root, index, owners = "AAA")) data.table::fread(file.path(index, "RawFileTable.AAA.csv"))root <- file.path(tempdir(), "gmsp-raw-file-example") index <- file.path(tempdir(), "gmsp-raw-file-index") unlink(c(root, index), recursive = TRUE) dir.create(file.path(root, "AAA", "E1", "S1", "raw.owner"), recursive = TRUE) dir.create(index) record <- list( Event = list(EventID = "E1"), Station = list(StationID = "S1"), Record = list( list(ComponentID = "H1", FileID = "H1.txt", NP = 4, dt = 0.01, Fs = 100, Units = "cm", HP = NA, LP = NA), list(ComponentID = "H2", FileID = "H2.txt", NP = 4, dt = 0.01, Fs = 100, Units = "cm", HP = NA, LP = NA), list(ComponentID = "UP", FileID = "UP.txt", NP = 4, dt = 0.01, Fs = 100, Units = "cm", HP = NA, LP = NA) ) ) jsonlite::write_json( record, file.path(root, "AAA", "E1", "S1", "raw.owner", "record.json"), auto_unbox = TRUE ) suppressMessages(buildRawFileTable(root, index, owners = "AAA")) data.table::fread(file.path(index, "RawFileTable.AAA.csv"))
For each station with raw/AT.<RID>.csv / .json, calls
getRawIntensities() to compute the 20 AT-derivable scalars per
direction via getIntensity(). Emits one row per
(RecordID, DIR) - three rows per record.
buildRawIntensityTable( path.records, path.index, owners = NULL, incremental = TRUE, force = FALSE )buildRawIntensityTable( path.records, path.index, owners = NULL, incremental = TRUE, force = FALSE )
path.records |
Absolute path to the records root. Required – no default. |
path.index |
Absolute path to the index root where per-owner CSVs are written. Required – no default. |
owners |
Character vector of |
incremental |
Logical. If |
force |
Logical. If |
Schema:
RecordID, DIR, OCID, AI, AId, AIu, ARMS, ATn, ATo, AZC, CAV, CAV5, D0575, D0595, D2080, Dmax, EPI, Fs, NP, PDI, PGA, TmA, dt
All amplitude scalars are in TARGET_UNITS = "mm"-derived units
(mm/s2, mm/s, etc. per getIntensity() contract).
Invisibly, the per-owner row counts.
root <- file.path(tempdir(), "gmsp-raw-intensity-table-example") index <- file.path(tempdir(), "gmsp-raw-intensity-table-index") unlink(c(root, index), recursive = TRUE) raw <- file.path(root, "AAA", "E1", "S1", "raw") dir.create(raw, recursive = TRUE) dir.create(index) t <- seq(0, 1, by = 0.01) data.table::fwrite( data.table::data.table( H1 = sin(2 * pi * t), H2 = 0.5 * cos(2 * pi * t), UP = 0.25 * sin(4 * pi * t) ), file.path(raw, "AT.R1.csv") ) jsonlite::write_json( list(RecordID = "R1", OwnerID = "AAA", EventID = "E1", StationID = "S1", NetworkID = "NW", DIR = c("H1", "H2", "UP"), OCID = c("H1", "H2", "UP"), NP = rep(length(t), 3), dt = 0.01, Fs = 100, Units = "mm"), file.path(raw, "AT.R1.json"), auto_unbox = TRUE ) suppressMessages(buildRawIntensityTable(root, index, owners = "AAA", incremental = FALSE)) data.table::fread(file.path(index, "RawIntensityTable.AAA.csv"))root <- file.path(tempdir(), "gmsp-raw-intensity-table-example") index <- file.path(tempdir(), "gmsp-raw-intensity-table-index") unlink(c(root, index), recursive = TRUE) raw <- file.path(root, "AAA", "E1", "S1", "raw") dir.create(raw, recursive = TRUE) dir.create(index) t <- seq(0, 1, by = 0.01) data.table::fwrite( data.table::data.table( H1 = sin(2 * pi * t), H2 = 0.5 * cos(2 * pi * t), UP = 0.25 * sin(4 * pi * t) ), file.path(raw, "AT.R1.csv") ) jsonlite::write_json( list(RecordID = "R1", OwnerID = "AAA", EventID = "E1", StationID = "S1", NetworkID = "NW", DIR = c("H1", "H2", "UP"), OCID = c("H1", "H2", "UP"), NP = rep(length(t), 3), dt = 0.01, Fs = 100, Units = "mm"), file.path(raw, "AT.R1.json"), auto_unbox = TRUE ) suppressMessages(buildRawIntensityTable(root, index, owners = "AAA", incremental = FALSE)) data.table::fread(file.path(index, "RawIntensityTable.AAA.csv"))
Scans <path.records>/<OwnerID>/<EventID>/<StationID>/raw/AT.*.json and
emits one row per RecordID to <path.index>/RawRecordTable.<OwnerID>.csv.
buildRawRecordTable(path.records, path.index, owners = NULL)buildRawRecordTable(path.records, path.index, owners = NULL)
path.records |
Absolute path to the records root. Required – no default. |
path.index |
Absolute path to the index root where per-owner CSVs are written. Required – no default. |
owners |
Character vector of |
Schema:
RecordID, EventID, StationID, OwnerID, NP, Fs, pad
where NP = max(json$NP) (post-align) and
pad = max(json$NP) - min(json$NP).
This table carries zero per-direction metadata: directional detail
(PGA, AI, ARMS, ...) lives in RawIntensityTable.<Owner>.csv. The
per-layer Raw* prefix leaves room for downstream processors to
emit their own <ProcessID>RecordTable.<Owner>.csv.
Invisibly, the per-owner row counts.
root <- file.path(tempdir(), "gmsp-raw-record-example") index <- file.path(tempdir(), "gmsp-raw-record-index") unlink(c(root, index), recursive = TRUE) raw <- file.path(root, "AAA", "E1", "S1", "raw") dir.create(raw, recursive = TRUE) dir.create(index) jsonlite::write_json( list(RecordID = "R1", OwnerID = "AAA", EventID = "E1", StationID = "S1", NP = c(4, 4, 4), Fs = 100), file.path(raw, "AT.R1.json"), auto_unbox = TRUE ) suppressMessages(buildRawRecordTable(root, index, owners = "AAA")) data.table::fread(file.path(index, "RawRecordTable.AAA.csv"))root <- file.path(tempdir(), "gmsp-raw-record-example") index <- file.path(tempdir(), "gmsp-raw-record-index") unlink(c(root, index), recursive = TRUE) raw <- file.path(root, "AAA", "E1", "S1", "raw") dir.create(raw, recursive = TRUE) dir.create(index) jsonlite::write_json( list(RecordID = "R1", OwnerID = "AAA", EventID = "E1", StationID = "S1", NP = c(4, 4, 4), Fs = 100), file.path(raw, "AT.R1.json"), auto_unbox = TRUE ) suppressMessages(buildRawRecordTable(root, index, owners = "AAA")) data.table::fread(file.path(index, "RawRecordTable.AAA.csv"))
End-to-end workflow that takes displacement time histories and produces a consistent set of velocity and acceleration, along with the displacement processed outputs. It regularizes sampling if needed, converts units (for raw data), chooses STFT parameters/resampling, applies robust edge tapering, performs spectral/time derivatives, and applies post-tapering/optional trimming.
DT2TS( .x, units.source, time = "t", Fmax = 16, kNyq = 3.125, resample = TRUE, derivate = "freq", units.target = "mm", NW = 128, OVLP = 75, flatZeros = FALSE, Astop0 = 1e-04, Apass0 = 0.001, AstopLP = 0.001, ApassLP = 0.98, trimZeros = FALSE, detrend = FALSE, regularize = FALSE, output = "TSL", verbose = FALSE, audit = TRUE, isRaw = TRUE, lowPass = TRUE )DT2TS( .x, units.source, time = "t", Fmax = 16, kNyq = 3.125, resample = TRUE, derivate = "freq", units.target = "mm", NW = 128, OVLP = 75, flatZeros = FALSE, Astop0 = 1e-04, Apass0 = 0.001, AstopLP = 0.001, ApassLP = 0.98, trimZeros = FALSE, detrend = FALSE, regularize = FALSE, output = "TSL", verbose = FALSE, audit = TRUE, isRaw = TRUE, lowPass = TRUE )
.x |
data.table. Input displacement records with a time column and one or more signal columns. |
units.source |
character. Source units for the input displacement when
|
time |
character. Name of the time column in the input (default |
Fmax |
numeric. Maximum frequency of interest (Hz). Guides STFT strategy and low-pass regularization during integration. |
kNyq |
numeric. Target Nyquist multiplier ( |
resample |
logical. Kept for compatibility; decision is made by the internal STFT strategy. |
derivate |
character. Derivative method for |
units.target |
character. Target units for acceleration-related outputs. |
NW |
integer. Nominal STFT window length (samples). May be adjusted. |
OVLP |
numeric. Window overlap percent. |
flatZeros |
logical. Apply edge tapering; if |
Astop0, Apass0
|
numeric. Normalized thresholds |
AstopLP, ApassLP
|
numeric. Anti-alias LP specs for resampling. |
trimZeros |
logical. If |
detrend |
logical. Remove mean before/after stages. |
regularize |
logical. Force time regularization of input if needed. |
output |
character. Early/short-circuit outputs (default: "TSL"): "DTo", "AT", "VT", "DT", "TSW", "TSL". |
verbose |
logical. Print diagnostic logs. |
audit |
logical. If |
isRaw |
logical. If |
lowPass |
logical. If |
Returns the requested object based on output.
t <- seq(0, 2, by = 0.02) x <- data.table::data.table( t = t, H1 = sin(2 * pi * t), H2 = 0.5 * cos(2 * pi * t), UP = 0.25 * sin(4 * pi * t) ) tsl <- DT2TS(x, units.source = "mm", Fmax = 4, NW = 16, audit = FALSE, isRaw = FALSE) head(tsl)t <- seq(0, 2, by = 0.02) x <- data.table::data.table( t = t, H1 = sin(2 * pi * t), H2 = 0.5 * cos(2 * pi * t), UP = 0.25 * sin(4 * pi * t) ) tsl <- DT2TS(x, units.source = "mm", Fmax = 4, NW = 16, audit = FALSE, isRaw = FALSE) head(tsl)
raw/<KIND>.<RecordID>.csv + <KIND>.<RecordID>.json.Pipeline: parseRecord -> mapComponents(rotate = FALSE) -> capture pre-align NP and
DIR/OCID mapping -> alignComponents -> pivot WIDE by provider
OCID -> md5-16 hash -> write. The function returns NULL and writes
nothing in three
skip paths:
Component classification cannot map the record (arrays with > 3 OCIDs, 2-component records, or records with no vertical channel in the vertical-component vocabulary).
The provider Units string cannot be normalised (.parseUnits
returns NA_character_, so the scale factor K is NULL).
kind is left at its default NULL and .parseKind(Key$Units)
returns NA_character_ (kind cannot be derived from Units).
extractRecord(.x, path, align = "max", kind = NULL)extractRecord(.x, path, align = "max", kind = NULL)
.x |
|
path |
Absolute path to the records root. The function writes outputs
under |
align |
|
kind |
Optional |
KIND is one of "AT" (acceleration), "VT" (velocity), "DT"
(displacement). By default it is derived from Key$Units via
.parseKind(). Pass kind explicitly to override – e.g.
kind = "VT" for blasting records whose Units may not be
machine-parseable.
Old contents of raw/ are unlinked before writing (idempotent).
JSON sidecar schema:
RecordID, OwnerID, EventID, StationID, NetworkID,
FileID (scalar = "<KIND>.<RID>.csv"),
DIR (array, ["H1","H2","UP"]),
OCID (array of 3, provider channels in DIR order),
NP (array of 3, pre-align NP per DIR),
PGA / PGV / PGD (array of 3, peak |s| per DIR, post-align;
field name derived from KIND[1] -> PGA/PGV/PGD),
dt, Fs, Units (scalars).
Absolute path to the written <KIND>.<RecordID>.csv, or NULL (skip).
root <- file.path(tempdir(), "gmsp-extract-example") unlink(root, recursive = TRUE) raw <- file.path(root, "ESM", "E1", "S1", "raw.owner") dir.create(raw, recursive = TRUE) writeLines(c("0 1", "0.01 2", "0.02 3"), file.path(raw, "N_acc.txt")) writeLines(c("0 2", "0.01 3", "0.02 4"), file.path(raw, "E_acc.txt")) writeLines(c("0 0", "0.01 1", "0.02 0"), file.path(raw, "Z_acc.txt")) rows <- data.table::data.table( OwnerID = "ESM", EventID = "E1", StationID = "S1", NetworkID = "NW", Units = "cm", FileID = c("N_acc.txt", "E_acc.txt", "Z_acc.txt") ) path <- extractRecord(rows, path = root, kind = "AT") basename(path)root <- file.path(tempdir(), "gmsp-extract-example") unlink(root, recursive = TRUE) raw <- file.path(root, "ESM", "E1", "S1", "raw.owner") dir.create(raw, recursive = TRUE) writeLines(c("0 1", "0.01 2", "0.02 3"), file.path(raw, "N_acc.txt")) writeLines(c("0 2", "0.01 3", "0.02 4"), file.path(raw, "E_acc.txt")) writeLines(c("0 0", "0.01 1", "0.02 0"), file.path(raw, "Z_acc.txt")) rows <- data.table::data.table( OwnerID = "ESM", EventID = "E1", StationID = "S1", NetworkID = "NW", Units = "cm", FileID = c("N_acc.txt", "E_acc.txt", "Z_acc.txt") ) path <- extractRecord(rows, path = root, kind = "AT") basename(path)
getIntensity() is a compatibility wrapper around TSL2IM(). It accepts
canonical long TSL input only; convert wide TSW input with TSW2TSL()
first.
getIntensity(.x, units.source, units.target = "mm", output = c("IML", "IMW"))getIntensity(.x, units.source, units.target = "mm", output = c("IML", "IMW"))
.x |
|
units.source |
character. Source units of the |
units.target |
character. Target units for the returned intensities.
Default |
output |
character. |
See TSL2IM().
t <- seq(0, 1, by = 0.01) tsl <- data.table::data.table(t = t, s = sin(2 * pi * t), ID = "AT", OCID = "H1") getIntensity(tsl, units.source = "mm")t <- seq(0, 1, by = 0.01) tsl <- data.table::data.table(t = t, s = sin(2 * pi * t), ID = "AT", OCID = "H1") getIntensity(tsl, units.source = "mm")
Reads raw/<KIND>.<RID>.csv (WIDE provider OCID columns) plus
raw/<KIND>.<RID>.json, builds a long TSL with ID set to the
file's KIND (AT, VT, or DT), and calls getIntensity() to
compute the per-direction intensity scalars.
getRawIntensities(path)getRawIntensities(path)
path |
Absolute path to a station folder containing |
Output (WIDE): one row per (RecordID, DIR) with the KIND-derivable
intensity columns.
DIR (H1/H2/UP) is recovered from the JSON sidecar's explicit
DIR/OCID mapping. The CSV column names remain provider OCID
values such as HHE, HNZ, or L/T/V.
Assumes signal already in TARGET_UNITS = "mm" (per extractRecord).
Wide data.table with intensity columns
(e.g. RecordID, DIR, OCID, AI, AIu, AId, PGA, ARMS, AZC, ATo, ATn, D0595, D2080, D0575, TmA, CAV, CAV5, NP, dt, Fs, Dmax, EPI, PDI
for AT inputs).
Returns NULL if the station has no raw/<KIND>.<RID>.csv / .json.
station <- file.path(tempdir(), "gmsp-raw-intensity-example") unlink(station, recursive = TRUE) raw <- file.path(station, "raw") dir.create(raw, recursive = TRUE) t <- seq(0, 1, by = 0.01) data.table::fwrite( data.table::data.table( H1 = sin(2 * pi * t), H2 = 0.5 * cos(2 * pi * t), UP = 0.25 * sin(4 * pi * t) ), file.path(raw, "AT.R1.csv") ) jsonlite::write_json( list(RecordID = "R1", OwnerID = "AAA", EventID = "E1", StationID = "S1", NetworkID = "NW", DIR = c("H1", "H2", "UP"), OCID = c("H1", "H2", "UP"), NP = rep(length(t), 3), dt = 0.01, Fs = 100, Units = "mm"), file.path(raw, "AT.R1.json"), auto_unbox = TRUE ) getRawIntensities(station)station <- file.path(tempdir(), "gmsp-raw-intensity-example") unlink(station, recursive = TRUE) raw <- file.path(station, "raw") dir.create(raw, recursive = TRUE) t <- seq(0, 1, by = 0.01) data.table::fwrite( data.table::data.table( H1 = sin(2 * pi * t), H2 = 0.5 * cos(2 * pi * t), UP = 0.25 * sin(4 * pi * t) ), file.path(raw, "AT.R1.csv") ) jsonlite::write_json( list(RecordID = "R1", OwnerID = "AAA", EventID = "E1", StationID = "S1", NetworkID = "NW", DIR = c("H1", "H2", "UP"), OCID = c("H1", "H2", "UP"), NP = rep(length(t), 3), dt = 0.01, Fs = 100, Units = "mm"), file.path(raw, "AT.R1.json"), auto_unbox = TRUE ) getRawIntensities(station)
IML2IMW() casts long intensity output from TSL2IM() / getIntensity()
to one row per metadata and OCID, with intensity measures as columns.
IML2IMW(.x, by = "auto")IML2IMW(.x, by = "auto")
.x |
Long intensity table with columns |
by |
Metadata columns to keep as row keys. The default |
A wide intensity data.table.
iml <- data.table::data.table(RecordID = "R1", OCID = "H1", ID = "AT", IM = "PGA", value = 1, units = "mm /s2") IML2IMW(iml)iml <- data.table::data.table(RecordID = "R1", OCID = "H1", ID = "AT", IM = "PGA", value = 1, units = "mm /s2") IML2IMW(iml)
Classifies a single three-component record, preserves provider channel names
in OCID, and records canonical processed directions in DIR
(H1, H2, UP). This helper is for processed products; raw extraction
continues to preserve provider OCID values.
mapComponents(DT, rotate = TRUE, output = c("long", "wide"))mapComponents(DT, rotate = TRUE, output = c("long", "wide"))
DT |
LONG |
rotate |
Logical scalar. If |
output |
|
A data.table in the requested output shape, or NULL when the
record cannot be mapped. Both shapes carry attr(out, "componentMap")
and attr(out, "rotate"); rotated outputs also carry
attr(out, "theta").
t <- seq(0, 1, by = 0.1) x <- data.table::rbindlist(list( data.table::data.table(t = t, OCID = "N", s = sin(2 * pi * t)), data.table::data.table(t = t, OCID = "E", s = 0.5 * cos(2 * pi * t)), data.table::data.table(t = t, OCID = "Z", s = 0.1 * sin(2 * pi * t)) )) mapped <- mapComponents(x, rotate = FALSE) head(mapped)t <- seq(0, 1, by = 0.1) x <- data.table::rbindlist(list( data.table::data.table(t = t, OCID = "N", s = sin(2 * pi * t)), data.table::data.table(t = t, OCID = "E", s = 0.5 * cos(2 * pi * t)), data.table::data.table(t = t, OCID = "Z", s = 0.1 * sin(2 * pi * t)) )) mapped <- mapComponents(x, rotate = FALSE) head(mapped)
Divides the signal column s by the peak amplitude of a reference quantity
(norm) for every (metadata, OCID) group. The same scale factor is applied
to all ID values within each group so that the physical relationship
between AT, VT, and DT is preserved.
The default norm = "PGA" scales by 1 / max(abs(s)) computed from
ID == "AT" rows, making max(abs(AT)) = 1 for every channel. The same
SF is then applied to the corresponding VT and DT rows.
Modifies .x in place; returns .x[].
normalizeTS(.x, norm = "PGA")normalizeTS(.x, norm = "PGA")
.x |
Canonical |
norm |
character. Reference quantity used to derive the scale factor.
|
.x with s scaled in place.
t <- seq(0, 1, by = 0.01) tsl <- data.table::rbindlist(list( data.table::data.table(t = t, s = 2 * sin(2 * pi * t), ID = "AT", OCID = "H1"), data.table::data.table(t = t, s = cos(2 * pi * t), ID = "VT", OCID = "H1"), data.table::data.table(t = t, s = sin(pi * t), ID = "DT", OCID = "H1") )) normalizeTS(tsl) max(abs(tsl[ID == "AT", s]))t <- seq(0, 1, by = 0.01) tsl <- data.table::rbindlist(list( data.table::data.table(t = t, s = 2 * sin(2 * pi * t), ID = "AT", OCID = "H1"), data.table::data.table(t = t, s = cos(2 * pi * t), ID = "VT", OCID = "H1"), data.table::data.table(t = t, s = sin(pi * t), ID = "DT", OCID = "H1") )) normalizeTS(tsl) max(abs(tsl[ID == "AT", s]))
Reads <path>/<OwnerID>/<EventID>/<StationID>/raw.owner/ according
to the owner's format and returns LONG (t, OCID, s). NPTS divergence
between components is not enforced.
parseRecord(.x, path)parseRecord(.x, path)
.x |
|
path |
Absolute path to the records root. The function expects
per-station files under
|
Quantity (ID = "AT"|"VT"|"DT") is NOT set here.
LONG data.table(t, OCID, s).
root <- file.path(tempdir(), "gmsp-parse-example") unlink(root, recursive = TRUE) raw <- file.path(root, "ESM", "E1", "S1", "raw.owner") dir.create(raw, recursive = TRUE) writeLines(c("0 1", "0.01 2", "0.02 3"), file.path(raw, "N_acc.txt")) writeLines(c("0 2", "0.01 3", "0.02 4"), file.path(raw, "E_acc.txt")) writeLines(c("0 0", "0.01 1", "0.02 0"), file.path(raw, "Z_acc.txt")) rows <- data.table::data.table( OwnerID = "ESM", EventID = "E1", StationID = "S1", FileID = c("N_acc.txt", "E_acc.txt", "Z_acc.txt") ) parseRecord(rows, path = root)root <- file.path(tempdir(), "gmsp-parse-example") unlink(root, recursive = TRUE) raw <- file.path(root, "ESM", "E1", "S1", "raw.owner") dir.create(raw, recursive = TRUE) writeLines(c("0 1", "0.01 2", "0.02 3"), file.path(raw, "N_acc.txt")) writeLines(c("0 2", "0.01 3", "0.02 4"), file.path(raw, "E_acc.txt")) writeLines(c("0 0", "0.01 1", "0.02 0"), file.path(raw, "Z_acc.txt")) rows <- data.table::data.table( OwnerID = "ESM", EventID = "E1", StationID = "S1", FileID = c("N_acc.txt", "E_acc.txt", "Z_acc.txt") ) parseRecord(rows, path = root)
PSL2PSW() casts canonical long spectra rows to wide columns such as
PSA.H1, PSV.H1, and SD.H1.
PSL2PSW(.x, by = "auto")PSL2PSW(.x, by = "auto")
.x |
Long spectra table with columns |
by |
Metadata columns to keep as row keys. The default |
A wide spectra data.table.
psl <- data.table::data.table(RecordID = "R1", OCID = "H1", Tn = 0.1, ID = "PSA", S = 1) PSL2PSW(psl)psl <- data.table::data.table(RecordID = "R1", OCID = "H1", Tn = 0.1, ID = "PSA", S = 1) PSL2PSW(psl)
PSW2PSL() melts spectra columns named <ID>.<OCID> back to canonical
long spectra rows. Derived RotD components such as D50 and D100 are
returned as ordinary OCID values.
PSW2PSL(.x, by = "auto", ids = c("PSA", "PSV", "SD"))PSW2PSL(.x, by = "auto", ids = c("PSA", "PSV", "SD"))
.x |
Wide spectra table with a |
by |
Metadata columns to keep as row keys. The default |
ids |
Preferred order for spectra IDs. Other IDs present in |
A canonical long spectra data.table.
psw <- data.table::data.table(RecordID = "R1", Tn = 0.1, PSA.H1 = 1) PSW2PSL(psw)psw <- data.table::data.table(RecordID = "R1", Tn = 0.1, PSA.H1 = 1) PSW2PSL(psw)
One file holds 3 components as parallel columns:
ACA (IGP Peru): cols Z N E after a header row.
ACB (CISMID Peru): cols T EW NS UD; the time column T is dropped.
LIS (UCR Costa Rica): cols N00E UPDO N90E after a ===DATA=== line.
readAC(file, type)readAC(file, type)
file |
Path to the file. |
type |
One of |
OCIDs come from the file's column header line.
LONG data.table(t, OCID, s).
file <- tempfile() writeLines(c( "MUESTREO : 100", " Z N E", "1 2 3", "4 5 6" ), file) readAC(file, type = "ACA")file <- tempfile() writeLines(c( "MUESTREO : 100", " Z N E", "1 2 3", "4 5 6" ), file) readAC(file, type = "ACA")
readTS() with kind = "AT".Thin wrapper around readTS(); see there for full semantics.
readAT(.x, path)readAT(.x, path)
.x |
|
path |
Absolute path to the records root. The function reads
per-station files under
|
See readTS().
root <- file.path(tempdir(), "gmsp-readat-example") unlink(root, recursive = TRUE) raw <- file.path(root, "AAA", "E1", "S1", "raw") dir.create(raw, recursive = TRUE) data.table::fwrite(data.table::data.table(H1 = c(1, 2)), file.path(raw, "AT.R1.csv")) jsonlite::write_json(list(dt = 0.01), file.path(raw, "AT.R1.json"), auto_unbox = TRUE) selection <- data.table::data.table( RecordID = "R1", OwnerID = "AAA", EventID = "E1", StationID = "S1" ) readAT(selection, path = root)root <- file.path(tempdir(), "gmsp-readat-example") unlink(root, recursive = TRUE) raw <- file.path(root, "AAA", "E1", "S1", "raw") dir.create(raw, recursive = TRUE) data.table::fwrite(data.table::data.table(H1 = c(1, 2)), file.path(raw, "AT.R1.csv")) jsonlite::write_json(list(dt = 0.01), file.path(raw, "AT.R1.json"), auto_unbox = TRUE) selection <- data.table::data.table( RecordID = "R1", OwnerID = "AAA", EventID = "E1", StationID = "S1" ) readAT(selection, path = root)
AT2 has a 4-line header ending with NPTS=/DT=. Line 2 holds the
direction as the last comma-separated token (e.g.,
Helena Montana-01, 10/31/1935, Carroll College, 180). Body has up
to 8 values per row in scientific notation; "stuck" negatives
(1.234-5.678) are split before parsing. Truncated at NPTS.
readAT2(file)readAT2(file)
file |
Path to the .AT2 file. |
LONG data.table(t, OCID, s).
file <- tempfile(fileext = ".AT2") writeLines(c( "header", "Event, date, station, H1", "units", "NPTS= 4, DT= 0.01 SEC", "1.0 2.0 3.0 4.0" ), file) readAT2(file)file <- tempfile(fileext = ".AT2") writeLines(c( "header", "Event, date, station, H1", "units", "NPTS= 4, DT= 0.01 SEC", "1.0 2.0 3.0 4.0" ), file) readAT2(file)
readTS() with kind = "DT".Thin wrapper around readTS(); see there for full semantics.
readDT(.x, path)readDT(.x, path)
.x |
|
path |
Absolute path to the records root. The function reads
per-station files under
|
See readTS().
root <- file.path(tempdir(), "gmsp-readdt-example") unlink(root, recursive = TRUE) raw <- file.path(root, "AAA", "E1", "S1", "raw") dir.create(raw, recursive = TRUE) data.table::fwrite(data.table::data.table(H1 = c(1, 2)), file.path(raw, "DT.R1.csv")) jsonlite::write_json(list(dt = 0.01), file.path(raw, "DT.R1.json"), auto_unbox = TRUE) selection <- data.table::data.table( RecordID = "R1", OwnerID = "AAA", EventID = "E1", StationID = "S1" ) readDT(selection, path = root)root <- file.path(tempdir(), "gmsp-readdt-example") unlink(root, recursive = TRUE) raw <- file.path(root, "AAA", "E1", "S1", "raw") dir.create(raw, recursive = TRUE) data.table::fwrite(data.table::data.table(H1 = c(1, 2)), file.path(raw, "DT.R1.csv")) jsonlite::write_json(list(dt = 0.01), file.path(raw, "DT.R1.json"), auto_unbox = TRUE) selection <- data.table::data.table( RecordID = "R1", OwnerID = "AAA", EventID = "E1", StationID = "S1" ) readDT(selection, path = root)
Parses the TXT output of an ISEE-compliant blasting seismograph
(Micromate, Vibra-Tech, GeoSonics). The format is the
International Society of Explosives Engineers (ISEE) Performance
Specifications for Blasting Seismographs and is declared in the
header line Version : V 10-90 Micromate ISEE.
readISEE(file)readISEE(file)
file |
Path to the ISEE TXT. |
Two firmware variants are supported transparently:
v10 / .TXT: header lines "<key> : <value>"
(space-colon-space), body tab-separated Tran Vert Long MicL.
v11 / .CSV: header lines "<key>","<value>" (CSV pair),
body comma-separated quoted "Tran","Vert","Long" (no MicL
present in the body).
Variant is auto-detected from the file content – the dispatch
is not by extension. Sample rate is read from a line matching
Sample\\s*Rate\\s*[: ,"]+<Fs>\\s*sps, which captures both
header styles. The body header is located by the regex
Tran[^A-Za-z]+Vert[^A-Za-z]+Long, also style-agnostic.
Column-to-OCID mapping (ISEE convention):
Tran (transverse) -> T
Vert (vertical) -> V
Long (longitudinal, radial blast-to-station) -> L
MicL (microphone, dB(L)) -> dropped when present (only in v10).
Velocity is in mm/s; dt = 1 / Fs is derived from the header.
No metadata other than Fs is read here – per-event scalars
(charge, distance to centroid, location) live in the blast
flatfile, not in the parser's contract.
To process an ISEE record end-to-end:
DT <- readISEE("UM12780_23_10_2025_15_22_8.TXT")
# DT now has columns (t, OCID, s) with OCIDs T/V/L; s in mm/s
From parseRecord() (the canonical entry point), the dispatch
happens via .OWNER_FORMAT["ISEE"] = "ISEE". To get a sidecar
record (raw/VT.<RID>.csv + JSON), call
extractRecord(.x, path) – the provider-string parser
(.parseUnits / .parseKind) accepts "mm/s" on the master row and
derives KIND = "VT" automatically, or pass kind = "VT" explicitly
to bypass derivation.
LONG data.table(t, OCID, s) with OCID in T/V/L
and s in mm/s. Three rows per sample, 3 * NP rows
total where NP is the per-channel sample count.
parseRecord(), extractRecord(), readAT2(),
readV2(), readAC().
file <- tempfile(fileext = ".TXT") writeLines(c( "Version : V 10-90 Micromate ISEE", "Sample Rate : 100 sps", "Tran\tVert\tLong\tMicL", "1\t2\t3\t90", "4\t5\t6\t91" ), file) readISEE(file)file <- tempfile(fileext = ".TXT") writeLines(c( "Version : V 10-90 Micromate ISEE", "Sample Rate : 100 sps", "Tran\tVert\tLong\tMicL", "1\t2\t3\t90", "4\t5\t6\t91" ), file) readISEE(file)
TRA/TRZ have a multi-column body after END_HEADER; the last column
is the corrected acceleration. TRB/TRC have a single-column body
after the Unidades: (TRB) or USER5 (TRC) line. OCID lives in the
header: Component: (TRA/TRZ), Componente: (TRB), STREAM: (TRC).
readTR(file, type)readTR(file, type)
file |
Path to the file. |
type |
One of |
LONG data.table(t, OCID, s).
file <- tempfile() writeLines(c( "Component: HNZ", "rate: 100", "END_HEADER", "skip1", "skip2", "1 2 3", "4 5 6" ), file) readTR(file, type = "TRA")file <- tempfile() writeLines(c( "Component: HNZ", "rate: 100", "END_HEADER", "skip1", "skip2", "1 2 3", "4 5 6" ), file) readTR(file, type = "TRA")
AT2TS() / VT2TS() / DT2TS() expect.Stacks raw/<KIND>.<RID>.csv from each record in .x, builds the time
axis t from dt (in raw/<KIND>.<RID>.json), and returns a single
data.table keyed at (RecordID, OwnerID, EventID, StationID, t).
readTS(.x, path, kind = c("AT", "VT", "DT"))readTS(.x, path, kind = c("AT", "VT", "DT"))
.x |
|
path |
Absolute path to the records root. The function reads
per-station files under
|
kind |
One of |
The sidecar shape produced by extractRecord() is identical across KINDs;
KIND only selects the file prefix. The CSV columns are provider OCID
values preserved by extractRecord(). Direction labels (H1/H2/UP)
live in the JSON sidecar mapping, not in the CSV header. Use the thin
wrappers readAT() / readVT() / readDT() at call sites where the KIND
is fixed.
Selection <- selectRecords(M, EventID = "...")
TS <- readTS(.x = Selection, path = "/path/to/records", kind = "VT")
# Sidecar declares units.source as the length base ("mm"); KIND is set by kind=.
TS[, VT2TS(.SD, units.source = "mm"), by = .(RecordID, OwnerID, EventID, StationID)]
data.table with columns
RecordID, OwnerID, EventID, StationID, t, <OCID columns>.
Records whose sidecars are missing are skipped.
root <- file.path(tempdir(), "gmsp-readts-example") unlink(root, recursive = TRUE) raw <- file.path(root, "AAA", "E1", "S1", "raw") dir.create(raw, recursive = TRUE) data.table::fwrite( data.table::data.table(H1 = c(1, 2), H2 = c(0, 1)), file.path(raw, "AT.R1.csv") ) jsonlite::write_json(list(dt = 0.01), file.path(raw, "AT.R1.json"), auto_unbox = TRUE) selection <- data.table::data.table( RecordID = "R1", OwnerID = "AAA", EventID = "E1", StationID = "S1" ) readTS(selection, path = root, kind = "AT")root <- file.path(tempdir(), "gmsp-readts-example") unlink(root, recursive = TRUE) raw <- file.path(root, "AAA", "E1", "S1", "raw") dir.create(raw, recursive = TRUE) data.table::fwrite( data.table::data.table(H1 = c(1, 2), H2 = c(0, 1)), file.path(raw, "AT.R1.csv") ) jsonlite::write_json(list(dt = 0.01), file.path(raw, "AT.R1.json"), auto_unbox = TRUE) selection <- data.table::data.table( RecordID = "R1", OwnerID = "AAA", EventID = "E1", StationID = "S1" ) readTS(selection, path = root, kind = "AT")
Time in column 1 (seconds), signal in column 2. No header. Separator
(space or tab) is auto-detected by fread. Trailing all-NA columns
(some providers leave a trailing tab -> phantom column) are dropped.
readTwoCol(file)readTwoCol(file)
file |
Path to the file. |
OCID extracted from filename. Three known patterns:
SEED-like: NET.STA.LOC.CHA__... -> CHA
CENA: <date>_<time>_NET.STA.CHA_AccTH -> CHA
CLSMD: <X>_acc.txt -> X
LONG data.table(t, OCID, s).
dir <- tempfile() dir.create(dir) file <- file.path(dir, "N_acc.txt") writeLines(c("0 1", "0.01 2"), file) readTwoCol(file)dir <- tempfile() dir.create(dir) file <- file.path(dir, "N_acc.txt") writeLines(c("0 1", "0.01 2"), file) readTwoCol(file)
Two CESMD variants:
Multi-channel V2 (.v2): blocks marked ^Corrected accelerogram,
8f10.6 body, ends at next points of veloc data. 1+ blocks.
Single-channel V2c (.V2c): line 1 = Corrected acceleration,
1E15.6 body (1 col/row). Marker acceleration pts carries NPTS.
samples/sec (last occurrence – DECIMATE > RESAMPLE) carries dt.
Body ends at End-of-data or EOF. OCID from Sta Chan ...: line.
readV2(file)readV2(file)
file |
Path to the .v2 / .V2c file. |
LONG data.table(t, OCID, s).
file <- tempfile(fileext = ".V2c") writeLines(c( "Corrected acceleration", "Sta Chan 1: HNZ", "100 samples/sec", "4 acceleration pts approx 0.04 secs", "1", "2", "3", "4", "End-of-data" ), file) readV2(file)file <- tempfile(fileext = ".V2c") writeLines(c( "Corrected acceleration", "Sta Chan 1: HNZ", "100 samples/sec", "4 acceleration pts approx 0.04 secs", "1", "2", "3", "4", "End-of-data" ), file) readV2(file)
Each component is a sequential block opened by Corrected accelerogram
(case-insensitive). Within a block, header ends 10 lines after
Displacement:. Body has 10 values per row in fixed-width form;
"stuck" negatives are split before parsing. Each component vector is
truncated at its own Number of points value.
readV2A(file)readV2A(file)
file |
Path to the .V2A file. |
OCIDs come from Component <X> lines in each block.
LONG data.table(t, OCID, s).
file <- tempfile(fileext = ".V2A") writeLines(c( "Corrected Accelerogram", "Component H1", "at 0.01 sec intervals", "Number of points 4", "Displacement:", rep("header", 10), "1 2 3 4", "Corrected Accelerogram", "Component H2", "Number of points 4", "Displacement:", rep("header", 10), "2 3 4 5", "Corrected Accelerogram", "Component UP", "Number of points 4", "Displacement:", rep("header", 10), "3 4 5 6" ), file) readV2A(file)file <- tempfile(fileext = ".V2A") writeLines(c( "Corrected Accelerogram", "Component H1", "at 0.01 sec intervals", "Number of points 4", "Displacement:", rep("header", 10), "1 2 3 4", "Corrected Accelerogram", "Component H2", "Number of points 4", "Displacement:", rep("header", 10), "2 3 4 5", "Corrected Accelerogram", "Component UP", "Number of points 4", "Displacement:", rep("header", 10), "3 4 5 6" ), file) readV2A(file)
readTS() with kind = "VT".Thin wrapper around readTS(); see there for full semantics.
readVT(.x, path)readVT(.x, path)
.x |
|
path |
Absolute path to the records root. The function reads
per-station files under
|
See readTS().
root <- file.path(tempdir(), "gmsp-readvt-example") unlink(root, recursive = TRUE) raw <- file.path(root, "AAA", "E1", "S1", "raw") dir.create(raw, recursive = TRUE) data.table::fwrite(data.table::data.table(H1 = c(1, 2)), file.path(raw, "VT.R1.csv")) jsonlite::write_json(list(dt = 0.01), file.path(raw, "VT.R1.json"), auto_unbox = TRUE) selection <- data.table::data.table( RecordID = "R1", OwnerID = "AAA", EventID = "E1", StationID = "S1" ) readVT(selection, path = root)root <- file.path(tempdir(), "gmsp-readvt-example") unlink(root, recursive = TRUE) raw <- file.path(root, "AAA", "E1", "S1", "raw") dir.create(raw, recursive = TRUE) data.table::fwrite(data.table::data.table(H1 = c(1, 2)), file.path(raw, "VT.R1.csv")) jsonlite::write_json(list(dt = 0.01), file.path(raw, "VT.R1.json"), auto_unbox = TRUE) selection <- data.table::data.table( RecordID = "R1", OwnerID = "AAA", EventID = "E1", StationID = "S1" ) readVT(selection, path = root)
Applies a 2-D PCA rotation to the two horizontal signal components
(DIR == "H1" and "H2") so that H1 aligns with the direction of
maximum variance and H2 is orthogonal. The vertical component
(DIR == "UP") is left untouched.
rotateComponents(DT)rotateComponents(DT)
DT |
LONG |
This step requires a LONG table already classified with DIR values
H1, H2, and UP. New processing code should usually call
mapComponents() with rotate = TRUE instead of calling this helper
directly.
The rotation angle theta is stored as attr(DT, "theta") in radians so
that extractRecord() can persist it in the JSON sidecar.
DT with s values updated in-place for DIR %in% c("H1","H2") and attr(DT, "theta") set. Returns DT unchanged
when the record does not have exactly two horizontal components.
t <- seq(0, 1, by = 0.1) x <- data.table::rbindlist(list( data.table::data.table(t = t, OCID = "N", DIR = "H1", s = sin(2 * pi * t)), data.table::data.table(t = t, OCID = "E", DIR = "H2", s = 0.5 * cos(2 * pi * t)), data.table::data.table(t = t, OCID = "Z", DIR = "UP", s = 0.1 * sin(2 * pi * t)) )) rotated <- rotateComponents(x) attr(rotated, "theta")t <- seq(0, 1, by = 0.1) x <- data.table::rbindlist(list( data.table::data.table(t = t, OCID = "N", DIR = "H1", s = sin(2 * pi * t)), data.table::data.table(t = t, OCID = "E", DIR = "H2", s = 0.5 * cos(2 * pi * t)), data.table::data.table(t = t, OCID = "Z", DIR = "UP", s = 0.1 * sin(2 * pi * t)) )) rotated <- rotateComponents(x) attr(rotated, "theta")
Filters buildMaster() output by any combination of RecordID,
EventID, StationID, OwnerID, then deduplicates to one row per
record. Output is the canonical selection shape consumed by
the readTS() family (readAT() / readVT() / readDT()) and
writeSelection().
selectRecords( M, RecordID = NULL, EventID = NULL, StationID = NULL, OwnerID = NULL )selectRecords( M, RecordID = NULL, EventID = NULL, StationID = NULL, OwnerID = NULL )
M |
master |
RecordID |
character. Filter by |
EventID |
character. Filter by |
StationID |
character. Filter by |
OwnerID |
character. Filter by |
Filter args are character vectors (length 1+). NULL means "no
restriction on this dimension". With all NULL, returns every
record in M – protect with explicit filters for non-trivial work.
For richer filters (magnitude, distance, intensity), filter M
first and pass the subset:
selectRecords(M[EventMagnitude > 7 & Repi < 100 & PGA > 600 & DIR == "H1"])
data.table(RecordID, OwnerID, EventID, StationID).
master <- data.table::data.table( RecordID = c("R1", "R1", "R2"), OwnerID = c("AAA", "AAA", "BBB"), EventID = c("E1", "E1", "E2"), StationID = c("S1", "S1", "S2"), DIR = c("H1", "H2", "H1") ) selectRecords(master, OwnerID = "AAA")master <- data.table::data.table( RecordID = c("R1", "R1", "R2"), OwnerID = c("AAA", "AAA", "BBB"), EventID = c("E1", "E1", "E2"), StationID = c("S1", "S1", "S2"), DIR = c("H1", "H2", "H1") ) selectRecords(master, OwnerID = "AAA")
Orchestrates VMD/EMD/EEMD decomposition for one canonical t/s signal and
returns either IMFs, a recomposed time series, or long/wide tables depending
on output.
TS2IMF() is a worker. It does not detect or own record/component grouping.
Use data.table grouping at the call site for TSL tables.
TS2IMF( .x, method = "vmd", K = 12, alpha = 2000, tau = 0, DC = TRUE, init = 0, tol = 1e-07, output = NULL, verbose = FALSE, boundary = "wave", stop.rule = "type5", noise.type = "gaussian", noise.amp = 5e-08, trials = 10, imf.remove = NULL, remove.Fo = NULL, keep.Fo = NULL, keep.Residue = TRUE )TS2IMF( .x, method = "vmd", K = 12, alpha = 2000, tau = 0, DC = TRUE, init = 0, tol = 1e-07, output = NULL, verbose = FALSE, boundary = "wave", stop.rule = "type5", noise.type = "gaussian", noise.amp = 5e-08, trials = 10, imf.remove = NULL, remove.Fo = NULL, keep.Fo = NULL, keep.Residue = TRUE )
.x |
|
method |
character. One of |
K |
integer. Number of IMFs (default |
alpha, tau, DC, init, tol
|
numeric. Parameters for VMD. |
output |
character or |
verbose |
logical. Engine verbosity (default |
boundary, stop.rule
|
character. EMD/EEMD parameters. |
noise.type, noise.amp, trials
|
parameters for EEMD. |
imf.remove |
character or integer. IMF selection (optional). Character
values remove explicit mode names such as |
remove.Fo, keep.Fo
|
numeric length-2 (Hz) frequency band rules (optional). |
keep.Residue |
logical. If TRUE (default), include |
Depending on output, returns TSL, TSW, IMF or a list with the
decomposition.
t <- seq(0, 1, by = 0.01) x <- data.table::data.table( t = t, s = sin(2 * pi * t) + 0.1 * sin(10 * pi * t) ) imf <- TS2IMF(x, method = "vmd", K = 2, output = "IMF") imft <- seq(0, 1, by = 0.01) x <- data.table::data.table( t = t, s = sin(2 * pi * t) + 0.1 * sin(10 * pi * t) ) imf <- TS2IMF(x, method = "vmd", K = 2, output = "IMF") imf
Given a long time-series table with columns for the record identifier,
OCID, ID, t, and s, converts amplitudes to units.target and
computes standard intensity measures grouped by record x OCID x ID.
Valid ID content is either AT only, or a complete time-series set with
AT, VT, and DT.
TSL2IM(.x, units.source, units.target = "mm", output = c("IML", "IMW"))TSL2IM(.x, units.source, units.target = "mm", output = c("IML", "IMW"))
.x |
|
units.source |
character. Source units of the |
units.target |
character. Target units for the returned intensities.
Default |
output |
character. |
A data.table. output = "IML" returns long rows with columns
<metadata cols>, OCID, ID, IM, value, units; output = "IMW" returns
wide intensity columns.
t <- seq(0, 1, by = 0.01) tsl <- data.table::rbindlist(list( data.table::data.table(t = t, s = sin(2 * pi * t), ID = "AT", OCID = "H1"), data.table::data.table(t = t, s = cos(2 * pi * t), ID = "VT", OCID = "H1"), data.table::data.table(t = t, s = sin(pi * t), ID = "DT", OCID = "H1") )) im <- TSL2IM(tsl, units.source = "mm") head(im)t <- seq(0, 1, by = 0.01) tsl <- data.table::rbindlist(list( data.table::data.table(t = t, s = sin(2 * pi * t), ID = "AT", OCID = "H1"), data.table::data.table(t = t, s = cos(2 * pi * t), ID = "VT", OCID = "H1"), data.table::data.table(t = t, s = sin(pi * t), ID = "DT", OCID = "H1") )) im <- TSL2IM(tsl, units.source = "mm") head(im)
TSL2PS() is the spectra helper for canonical TSL tables produced by
AT2TS(), VT2TS(), and DT2TS(). It derives grouping keys from the
TSL schema instead of exposing BY or column-name arguments.
TSL2PS( .x, xi = 0.05, Tn = NULL, output = "PSL", D50 = FALSE, D100 = FALSE, nTheta = 180L )TSL2PS( .x, xi = 0.05, Tn = NULL, output = "PSL", D50 = FALSE, D100 = FALSE, nTheta = 180L )
.x |
Canonical |
xi |
numeric. Damping ratio(s) |
Tn |
numeric vector. Natural periods in seconds. Must not include |
output |
character. One of |
D50 |
logical scalar. If |
D100 |
logical scalar. If |
nTheta |
integer. Number of rotation angles in |
A data.table.
output = "PSL" returns a long table with metadata columns, OCID,
Tn, spectral ID ("PSA", "PSV", "SD"), and S. If xi is a
vector, the output also includes xi.
output = "PSW" returns a wide table with metadata columns, Tn, and
spectral component columns such as PSA.H1, PSV.H1, and SD.H1. If
xi is a vector, the output also includes xi.
If requested, D50 and D100 appear as ordinary component suffixes such as
PSA.D50 and PSA.D100.
t <- seq(0, 1, by = 0.01) tsl <- data.table::rbindlist(list( data.table::data.table(t = t, s = sin(2 * pi * t), ID = "AT", OCID = "H1"), data.table::data.table(t = t, s = cos(2 * pi * t), ID = "VT", OCID = "H1"), data.table::data.table(t = t, s = sin(pi * t), ID = "DT", OCID = "H1"), data.table::data.table(t = t, s = sin(2 * pi * t + 0.3), ID = "AT", OCID = "H2"), data.table::data.table(t = t, s = cos(2 * pi * t + 0.4), ID = "VT", OCID = "H2"), data.table::data.table(t = t, s = sin(pi * t + 0.2), ID = "DT", OCID = "H2") )) ps <- TSL2PS(tsl, Tn = c(0.1, 0.2)) head(ps) rot <- TSL2PS(tsl, Tn = 0.1, D50 = TRUE, D100 = TRUE, nTheta = 6L) rot[OCID %in% c("D50", "D100")]t <- seq(0, 1, by = 0.01) tsl <- data.table::rbindlist(list( data.table::data.table(t = t, s = sin(2 * pi * t), ID = "AT", OCID = "H1"), data.table::data.table(t = t, s = cos(2 * pi * t), ID = "VT", OCID = "H1"), data.table::data.table(t = t, s = sin(pi * t), ID = "DT", OCID = "H1"), data.table::data.table(t = t, s = sin(2 * pi * t + 0.3), ID = "AT", OCID = "H2"), data.table::data.table(t = t, s = cos(2 * pi * t + 0.4), ID = "VT", OCID = "H2"), data.table::data.table(t = t, s = sin(pi * t + 0.2), ID = "DT", OCID = "H2") )) ps <- TSL2PS(tsl, Tn = c(0.1, 0.2)) head(ps) rot <- TSL2PS(tsl, Tn = 0.1, D50 = TRUE, D100 = TRUE, nTheta = 6L) rot[OCID %in% c("D50", "D100")]
TSL2TSW() casts a canonical long TSL table with t, s, ID, and
OCID columns to wide TSW form with columns such as AT.H1, VT.H1,
and DT.H1.
TSL2TSW(.x, by = "auto", ids = c("AT", "VT", "DT"))TSL2TSW(.x, by = "auto", ids = c("AT", "VT", "DT"))
.x |
Canonical |
by |
Metadata columns to keep as row keys. The default |
ids |
Preferred order for signal IDs in the output columns. Other IDs
present in |
A wide data.table keyed by <metadata>, t.
tsl <- data.table::data.table(RecordID = "R1", OCID = "H1", ID = "AT", t = c(0, 0.01), s = c(1, 2)) TSL2TSW(tsl)tsl <- data.table::data.table(RecordID = "R1", OCID = "H1", ID = "AT", t = c(0, 0.01), s = c(1, 2)) TSL2TSW(tsl)
TSW2TSL() melts wide time-series columns named <ID>.<OCID> back to
canonical TSL rows.
TSW2TSL(.x, by = "auto", ids = c("AT", "VT", "DT"))TSW2TSL(.x, by = "auto", ids = c("AT", "VT", "DT"))
.x |
Wide |
by |
Metadata columns to keep as row keys. The default |
ids |
Preferred order for signal IDs in the output columns. Other IDs
present in |
A canonical long data.table with metadata columns, OCID, ID,
t, and s.
tsw <- data.table::data.table(RecordID = "R1", t = c(0, 0.01), AT.H1 = c(1, 2)) TSW2TSL(tsw)tsw <- data.table::data.table(RecordID = "R1", t = c(0, 0.01), AT.H1 = c(1, 2)) TSW2TSL(tsw)
End-to-end workflow that takes velocity time histories and produces a consistent set of acceleration, velocity, and displacement time series. It optionally regularizes sampling, converts units (for raw data), selects optimal STFT parameters and resampling strategy, applies robust edge tapering, performs spectral-domain derivation and integration, yields post-tapering and optional trimming.
VT2TS( .x, units.source, time = "t", Fmax = 16, kNyq = 3.125, resample = TRUE, derivate = "freq", units.target = "mm", NW = 128, OVLP = 75, flatZeros = FALSE, Astop0 = 1e-04, Apass0 = 0.001, AstopLP = 0.001, ApassLP = 0.98, trimZeros = FALSE, detrend = FALSE, regularize = FALSE, verbose = FALSE, audit = TRUE, output = "TSL", isRaw = TRUE, lowPass = TRUE )VT2TS( .x, units.source, time = "t", Fmax = 16, kNyq = 3.125, resample = TRUE, derivate = "freq", units.target = "mm", NW = 128, OVLP = 75, flatZeros = FALSE, Astop0 = 1e-04, Apass0 = 0.001, AstopLP = 0.001, ApassLP = 0.98, trimZeros = FALSE, detrend = FALSE, regularize = FALSE, verbose = FALSE, audit = TRUE, output = "TSL", isRaw = TRUE, lowPass = TRUE )
.x |
data.table. Input velocity records with a time column and one or more signal columns. |
units.source |
character. Source units for input velocity when |
time |
character. Name of the time column in the input (default |
Fmax |
numeric. Maximum frequency of interest (Hz). Guides STFT strategy. |
kNyq |
numeric. Target Nyquist multiplier ( |
resample |
logical. Kept for compatibility; the actual decision is made by
the internal STFT strategy based on |
derivate |
character. Derivative method for |
units.target |
character. Target units for output acceleration. Default: "mm". |
NW |
integer. Nominal STFT window length (samples). May be adjusted. |
OVLP |
numeric. Window overlap percent. |
flatZeros |
logical. Apply edge tapering. If |
Astop0, Apass0
|
numeric. Normalized thresholds |
AstopLP, ApassLP
|
numeric. Anti-alias LP specifications for resampling. |
trimZeros |
logical. If |
detrend |
logical. Remove mean before/after stages. |
regularize |
logical. Force time regularization of input if needed. |
verbose |
logical. Print diagnostic logs. |
audit |
logical. If |
output |
character. Early/short-circuit outputs (default: "TSL"): "VTo", "AT", "VT", "DT", "TSW", "TSL". |
isRaw |
logical. If |
lowPass |
logical. If |
Returns the requested object based on output.
t <- seq(0, 2, by = 0.02) x <- data.table::data.table( t = t, H1 = sin(2 * pi * t), H2 = 0.5 * cos(2 * pi * t), UP = 0.25 * sin(4 * pi * t) ) tsl <- VT2TS(x, units.source = "mm", Fmax = 4, NW = 16, audit = FALSE, isRaw = FALSE) head(tsl)t <- seq(0, 2, by = 0.02) x <- data.table::data.table( t = t, H1 = sin(2 * pi * t), H2 = 0.5 * cos(2 * pi * t), UP = 0.25 * sin(4 * pi * t) ) tsl <- VT2TS(x, units.source = "mm", Fmax = 4, NW = 16, audit = FALSE, isRaw = FALSE) head(tsl)
selection/<name>.csv / .json.Deduplicates (OwnerID, EventID, StationID) so the CSV
carries one row per station folder (= one record). The JSON sidecar
captures audit metadata: name, timestamp, total hits, hits per owner.
writeSelection(DT, name, path)writeSelection(DT, name, path)
DT |
subset of the master |
name |
identifier for the selection, used as filename stem. |
path |
Absolute path to the directory where |
The CSV is the canonical input contract for any downstream
orchestrator that iterates over a selection: each row identifies one
(OwnerID, EventID, StationID) station folder under whichever
records root the orchestrator was given.
Invisibly, the deduplicated selection data.table.
x <- data.table::data.table( OwnerID = c("AAA", "AAA"), EventID = c("E1", "E1"), StationID = c("S1", "S1"), DIR = c("H1", "H2") ) path <- file.path(tempdir(), "gmsp-selection-example") unlink(path, recursive = TRUE) suppressMessages(writeSelection(x, name = "demo", path = path)) list.files(path)x <- data.table::data.table( OwnerID = c("AAA", "AAA"), EventID = c("E1", "E1"), StationID = c("S1", "S1"), DIR = c("H1", "H2") ) path <- file.path(tempdir(), "gmsp-selection-example") unlink(path, recursive = TRUE) suppressMessages(writeSelection(x, name = "demo", path = path)) list.files(path)