diff --git a/docs/index.html b/docs/index.html index 6514510..0a59631 100644 --- a/docs/index.html +++ b/docs/index.html @@ -1,52 +1,54 @@ sac-format -

sac-format
C++20 SAC-file Library
V0.4.0
User Manual

Table of Contents

Windows 2022 Build Status +

sac-format
C++20 SAC-file Library
V0.4.0
User Manual

Windows 2022 Build Status Ubuntu 22.04 Build Status macOS 13 Build Status Code Coverage Codacy grade CodeFactor -CPP-Linter Results

1. Introduction

sac-format is a single-header statically linked library designed to make working +CPP-Linter Results

1. Introduction

sac-format is a single-header statically linked library designed to make working with binary SAC-files as easy as possible. Written in C++20, it follows a modern and easy to read programming-style while providing the high performance brought -by C++.

sac-format’s developed on GitHub!

Download an offline version of the documentation (PDF).

1.1. Why sac-format

sac-format is Free and Open Source Software (FOSS) released under the MIT +by C++.

sac-format’s developed on GitHub!

Download an offline version of the documentation (PDF).

1.1. Why sac-format

sac-format is Free and Open Source Software (FOSS) released under the MIT license. Anyone can use it, for any purpose (including proprietary software), anywhere in the world. sac-format is operating system agnostic and confirmed -working on Windows, macOS, and Linux systems.

1.1.1. Safe

sac-format is safe—it conforms to a strict set of C++ programming guidelines, +working on Windows, macOS, and Linux systems.

1.1.1. Safe

sac-format is safe—it conforms to a strict set of C++ programming guidelines, chosen to ensure safe code-execution. The guideline conformance list is in cpp-linter.yml and can be cross-referenced against this master list. Results of conformance checking are here.

Testing is an important part of software development; the sac-format library is extensively tested using the Catch2 testing framework. Everything from low-level binary conversions to high-level Trace reading/writing are tested and confirmed -working. Check and run the tests yourself. See the Testing section for more -information.

1.1.2. Fast

sac-format is fast—it’s written in C++, carefully optimized, and extensively +working. Check and run the tests yourself. See the Testing section for more +information.

1.1.2. Fast

sac-format is fast—it’s written in C++, carefully optimized, and extensively benchmarked. You can run the benchmarks yourself to find out how sac-format -performs on your system. See the Benchmarking section for more information.

1.1.3. Easy

sac-format is easy—single-header makes integration in any project simple. +performs on your system. See the Benchmarking section for more information.

1.1.3. Easy

sac-format is easy—single-header makes integration in any project simple. Building is a breeze with CMake, even on different platforms. Object-oriented -design makes use easy and intuitive. See the Quickstart section to get up and -running.

1.1.4. Small

sac-format is small—in total (header + implementation–excluding comments) -it’s fewer than 2000 lines of code. Small size opens the door to using on any -sort of hardware (old or new) and makes it easy to expand upon.

1.1.5. Documented

sac-format is extensively documented—both online and in the code. Nothing’s +design makes use easy and intuitive. See the Quickstart section to get up and +running.

1.1.4. Small

sac-format is small—in total (header + implementation–excluding comments) the +library is under 2100∗ lines of code. Small size opens the door to using on any +sort of hardware (old or new) and makes it easy to expand upon.

∗ This value includes only the library, excluding all testing/benchmarking +and example codes. Including utests.cpp, benchmark.cpp, util.hpp, the example +program (list_sac), and sac-format totals just over 5100 lines of code.

1.1.5. Documented

sac-format is extensively documented—both online and in the code. Nothing’s hidden—nothing’s obscured. Curious how something works? Check the -documentation and in-code comments.

1.1.6. Transparent

sac-format is transparent—all analysis and coverage information is publicly -available online.

1.1.7. Trace Class

sac-format includes the Trace class for seismic traces, providing high-level +documentation and in-code comments.

1.1.6. Transparent

sac-format is transparent—all analysis and coverage information is publicly +available online.

1.1.7. Trace Class

sac-format includes the Trace class for seismic traces, providing high-level object-oriented abstraction to seismic data. With the Trace class, you don’t need to worry about manually reading SAC-files word-by-word. It’s compatible with v6 and v7 SAC-files and can automatically detect the version upon reading. File output defaults to v7 SAC-files and there is a legacy_write function for v6 -output.

1.1.8. Low-Level I/O

If you want to roll your own SAC-file processing workflow you can use the +output.

1.1.8. Low-Level I/O

If you want to roll your own SAC-file processing workflow you can use the low-level I/O functionality built into sac-format. All functions tested and -confirmed working—they’re used to build the Trace class!

2. Quickstart

2.1. Manual Instructions

2.1.1. Build Instructions

Building is as easy as cloning the repository, running CMake for your preferred -build tool, and then building.

  1. GCC
    git clone https://github.com/arbCoding/sac-format.git
    +confirmed working—they’re used to build the Trace class!

2. Quickstart

2.1. Manual Instructions

2.1.1. Build Instructions

Building is as easy as cloning the repository, running CMake for your preferred +build tool, and then building.

  1. GCC
    git clone https://github.com/arbCoding/sac-format.git
     cmake --preset gcc-release
     cmake --build ./build/release/gcc
    -
  2. Clang
    git clone https://github.com/arbCoding/sac-format.git
    +
  3. Clang
    git clone https://github.com/arbCoding/sac-format.git
     cmake --preset clang-release
     cmake --build ./build/release/clang
    -

2.1.2. Use

To use link to the compiled library (libsac-format.a on Linux/macOS, -libsac-format.lib on Windows) and include src/sac_format.hpp.

2.2. Example Programs

2.2.1. list_sac

list_sac is a command line program that takes a single SAC-file as its input +

2.1.2. Use

To use link to the compiled library (libsac-format.a on Linux/macOS, +libsac-format.lib on Windows) and include src/sac_format.hpp.

2.2. Example Programs

2.2.1. list_sac

list_sac is a command line program that takes a single SAC-file as its input argument. It reads the SAC-file and outputs the header/footer information, as -well as the true size of the data1 and data2 vectors.

2.3. CMake Integration

To integrate sac-format into your CMake project, add it to your CMakeLists.txt.

include(FetchContent)
+well as the true size of the data1 and data2 vectors.

2.3. CMake Integration

To integrate sac-format into your CMake project, add it to your CMakeLists.txt.

include(FetchContent)
 set(FETCHCONTENT_UPDATES_DISCONNECTED TRUE)
 FetchContent_Declare(sac-format
     GIT_REPOSITORY https://github.com/arbCoding/sac-format
@@ -63,7 +65,7 @@
 
 target_link_libraries_library(your_executable
     PRIVATE sac-format)
-

2.4. Example

2.4.1. Reading and Writing

#include <filesystem>
+

2.4. Example

2.4.1. Reading and Writing

#include <filesystem>
 #include <iostream>
 #include <sac_format.hpp>
 
@@ -84,8 +86,8 @@
     fs::remove(file);
     return EXIT_SUCCESS;
 }
-

3. Documentation

3.1. Trace class

The Trace class provides easy access to SAC-files in C++. Each SAC-file is a -Trace; therefore, each Trace object is a seismic trace (seismogram).

3.1.1. Reading SAC

SAC-files can be read in by using the parameterized constructor with a +

3. Documentation

3.1. Trace class

The Trace class provides easy access to SAC-files in C++. Each SAC-file is a +Trace; therefore, each Trace object is a seismic trace (seismogram).

3.1.1. Reading SAC

SAC-files can be read in by using the parameterized constructor with a std::filesystem::path (<filesystem>) or a std::string (<string>) variable that corresponds to the location of the SAC-file.

For example:

#include <filesystem>
 #include <sac_foramt.hpp>
@@ -95,9 +97,34 @@
   sacfmt::Trace anmo = Trace(my_file);
   return EXIT_SUCCESS;
 }
-

3.1.2. Writing SAC

Writing SAC files can be done using one of two write functions.

  1. v7 files

    Use write (for example trace.write(filename)).

  2. v6 files

    Use legacy_write (for example trace.legacy_write(filename)).

3.1.3. Getters and Setters

Every SAC variable is accessed via getters and setters of the same name.

  1. Example Getters
    • trace.npts()
    • trace.data1()
    • trace.kstnm()
  2. Example Setters
    • trace.kevnm("Event 1")
    • trace.evla(32.89)
    • trace.mag(3.21)

3.1.4. Internal Structure

The SAC-trace stores the data internally in a series of pre-allocated -std::array (<array>) container objects. Getters and setters access these via -a lookup table. The internal components are below:

  1. Lookup Table

    sac_map

  2. floats array
  3. doubles array
  4. ints array
  5. bools array
  6. strings array
  7. data array

3.1.5. Convenience Methods

  • calc_geometry

Calculate gcarc, dist, az, and baz assuming spherical Earth.

trace.stla(45.3);
+

3.1.2. Writing SAC

Writing SAC files can be done using one of two write functions.

  1. v7 files

    Use write (for example trace.write(filename)).

  2. v6 files

    Use legacy_write (for example trace.legacy_write(filename)).

3.1.3. Getters and Setters

Every SAC variable is accessed via getters and setters of the same name.

  1. Example Getters
    • trace.npts()
    • trace.data1()
    • trace.kstnm()
  2. Example Setters
    • trace.kevnm("Event 1")
    • trace.evla(32.89)
    • trace.mag(3.21)
  3. Setter rules

    Most of the setters are only constrained by the parameter type +(single-precision, double-precision, boolean, etc.). Some setters are +constrained by additional rules.

    1. Required for sanity

      Rules here are required because the sac-format library assumes them (not +strictly required by the SAC format standard). For instance, the geometric +functions assume certain bounds on latitudes and longitudes. sac-format +automatically imposes these rules.

      1. stla(input)

        Limited to \([-90, 90]\) degrees, input that is outside that range is reduced +using circular symmetry.

      2. stlo(input)

        Limited to \([-180, 180]\) degrees, input that is outside that range is reduced +using circular symmetry.

      3. evla(input)

        Limited to \([-90, 90]\) degrees, input that is outside that range is reduced +using circular symmetry.

      4. evlo(input)

        Limited to \([-180, 180]\) degrees, input that is outside that range is reduced +using circular symmetry.

    2. Require for safety

      Rules here are required by the SAC format standard. sac-format automatically +imposes these rules to prevent the creation of corrupt sac-files.

      1. npts(input)

        Because npts defines the size of the data vectors, changing this value will +change the size of data1 and data2∗. Increasing npts resizes the vectors +(std::vector::resize) by placing zeros at the end of the vectors. Reducing npts +resizes the vectors down to the first npts values.

        Therefore, care must be taken to maintain separate copies of data1 and +data2∗ if you plan to manipulate the original data after resizing.

        ∗ data2 has npts only if it is legal, otherwise it is of size 0.

      2. leven(input)

        Changing the value of leven potentially changes the legality of data2, it also +potentially affects the value of iftype.

        If iftype\(>1\), then leven must be true (evenly sampled data). Therefore, if +leven is made false in this scenario (unevenly sampled data) then iftype becomes +unset∗.

        If changing leven makes data2 legal∗∗, then data2 is resized to have +npts zeros.

        ∗ The SAC format defines the unset values for all data-types. For integers +(like iftype) it is the integer value -12345.

        ∗∗ If data2 was already legal, then it is unaffected.

      3. iftype(input)

        Changing the value of iftype poentially changes the legality of data2, it also potentially affects the value of leven.

        If leven is false, then iftype must be either 1 or unset. Therefore, changing +iftype to have a value \(>1\) requires that leven becomes true (evenly sampled +data).

        If changing iftype makes data2 legal∗, then data2 is resized to have npts zeros.

        ∗ If data2 was already legal, then it is unaffected.

      4. data1(input)

        If the size of data1 is changed, then npts must change to reflect the new size. +If data2 is legal, this adjusts its size to match as well.

      5. data2(input)

        If the size of data2 is changed to be larger than 0 and it is illegal, it is +made legal by setting iftype(2) (spectral-data).

        When the size of data2 changes, npts is updated to the new size and data1 is +resized to match.

        If data2 is made illegal, its size is reduced to 0 while npts and data1 are +unaffected.

3.1.4. Internal Structure

The SAC-trace stores the data internally in a series of pre-allocated +std::array (<array>) container objects. Getters and setters access these via +a lookup table. The internal components are below:

  1. Lookup Table

    sac_map

  2. floats array
  3. doubles array
  4. ints array
  5. bools array
  6. strings array
  7. data array

3.1.5. Convenience Methods

  • calc_geometry

Calculate gcarc, dist, az, and baz assuming spherical Earth.

trace.stla(45.3);
 trace.stlo(34.5);
 trace.evla(18.5);
 trace.evlo(-34);
@@ -110,8 +137,8 @@
 
  • date

Return std::string formatted as YYYY-JJJ from nzyear and nzjday.

std::string date{trace.date()};
 
  • time

Return std::string formatted as HH:MM:SS.xxx from nzhour, nzmin, nzsec, and nzmsec.

std::string time{trace.time()};
-

3.1.6. Exceptions

sac-format throws exceptions of type sacfmt::io_error (inherits -std::exception) in the event of a failure to read/write a SAC-file.

3.2. Convenience Functions

  • degrees_to_radians

Convert decimal degrees to radians.

double radians{sacfmt::degrees_to_radians(degrees)};
+

3.1.6. Exceptions

sac-format throws exceptions of type sacfmt::io_error (inherits +std::exception) in the event of a failure to read/write a SAC-file.

3.2. Convenience Functions

  • degrees_to_radians

Convert decimal degrees to radians.

double radians{sacfmt::degrees_to_radians(degrees)};
 
  • radians_to_degrees

Convert radians to decimal degrees.

double degrees{sacfmt::radians_to_degrees(radians)};
 
  • gcarc

Calculate great-circle arc distance (spherical planet).

double gcarc{sacfmt::gcarc(latitude1, longitude1, latitude2, longitude2)};
 
  • azimuth

Calculate azimuth between two points (spherical planet).

double azimuth{sacfmt::azimuth(latitude2, longitude2, latitude1, longitude1)};
@@ -120,64 +147,64 @@
 
  • limit_180

Take arbitrary value of degrees and unwrap to \([-180, 180]\). Useful for longitude.

double degrees_limited{sacfmt::limit_180(degrees)};
 
  • limit_90

Take arbitrary value of degrees and unwrap to \([-90, 90]\). Useful for latitude.

double degrees_limited{sacfmt::limit_90(degrees)};
-

3.3. Low-Level I/O

Low-level I/O functions are discussed below.

  1. Binary conversion
    1. int_to_binary and binary_to_int

      Conversion pair for binary representation of integer values.

      const int input{10};
      +

3.3. Low-Level I/O

Low-level I/O functions are discussed below.

  1. Binary conversion
    1. int_to_binary and binary_to_int

      Conversion pair for binary representation of integer values.

      const int input{10};
       // sacfmt::word_one is alias for std::bitset<32> (one word)
       sacfmt::word_one binary{sacfmt::int_to_binary(input)};
       const int output{sacfmt::binary_to_int(binary)};
       std::cout << (input == output) << '\n';
      -
    2. float_to_binary and binary_to_float

      Conversion pair for binary representation of floating-point values.

      const float input{5F};
      +
    3. float_to_binary and binary_to_float

      Conversion pair for binary representation of floating-point values.

      const float input{5F};
       sacfmt::word_one binary{sacfmt::float_to_binary(input)};
       const float output{sacfmt::binary_to_float(binary)};
       std::cout << (input == output) << '\n';
      -
    4. double_to_binary and binary_to_double

      Conversion pair for binary representation of double-precision values.

      const double input{1e5};
      +
    5. double_to_binary and binary_to_double

      Conversion pair for binary representation of double-precision values.

      const double input{1e5};
       // sacfmt::word_two is alias for std::bitset<64> (two words)
       sacfmt::word_two binary{sacfmt::double_to_binary(input)};
       const double output{sacfmt::binary_to_double(binary)};
       std::cout << (input == output) << '\n';
      -
    6. string_to_binary and binary_to_string

      Conversion pair for binary representation of two-word (regular) string values.

      const std::string input{"NmlStrng"};
      +
    7. string_to_binary and binary_to_string

      Conversion pair for binary representation of two-word (regular) string values.

      const std::string input{"NmlStrng"};
       sacfmt::word_two binary{sacfmt::string_to_binary(input)};
       const std::string output{sacfmt::binary_to_string(binary)};
       std::cout << (input == output) << '\n';
      -
    8. long_string_to_binary and binary_to_long_string

      Conversion pair for binary representation of four-word (only kstnm) string +

  • long_string_to_binary and binary_to_long_string

    Conversion pair for binary representation of four-word (only kstnm) string values.

    const std::string input{"The Long String"};
     // sacfmt::word_four is alias for std::bitset<128> (four words)
     sacfmt::word_four binary{sacfmt::long_string_to_binary(input)};
     const std::string output{sacfmt::binary_to_long_string(binary)};
     std::cout << (input == output) << '\n';
    -
  • Reading/Writing

    NOTE that care must be taken when using them to ensure that safe input is +

  • Reading/Writing

    NOTE that care must be taken when using them to ensure that safe input is provided; the Trace class ensures safe I/O, low-level I/O functions do not -necessarily ensure safety.

    1. read_word, read_two_words, read_four_words, and read_data

      Functions to read one-, two-, and four-word variables (depending on the header) -and an arbitrary amount of binary data (exclusive to data1 and data2).

    2. convert_to_word, convert_to_words, and bool_to_word

      Takes objects and converts them into std::vector<char> (convert_to_word and -bool_to_word) or std::array<char, N> (convert_to_words, N = # of words).

    3. write_words

      Writes input words (as std::vector<char>) to a binary SAC-file.

  • Utility
    1. concat_words

      Concatenates words taking into account the system endianness.

    2. bits_string and string_bits

      Template function that performs conversion of binary strings of arbitrary length -to an arbitrary number of words.

    3. remove_leading_spaces and remove_trailing_spaces

      Remove leading and trailing blank spaces from strings assuming ASCII convention +necessarily ensure safety.

      1. read_word, read_two_words, read_four_words, and read_data

        Functions to read one-, two-, and four-word variables (depending on the header) +and an arbitrary amount of binary data (exclusive to data1 and data2).

      2. convert_to_word, convert_to_words, and bool_to_word

        Takes objects and converts them into std::vector<char> (convert_to_word and +bool_to_word) or std::array<char, N> (convert_to_words, N = # of words).

      3. write_words

        Writes input words (as std::vector<char>) to a binary SAC-file.

    4. Utility
      1. concat_words

        Concatenates words taking into account the system endianness.

      2. bits_string and string_bits

        Template function that performs conversion of binary strings of arbitrary length +to an arbitrary number of words.

      3. remove_leading_spaces and remove_trailing_spaces

        Remove leading and trailing blank spaces from strings assuming ASCII convention (space character is integer 32, below that value are control characters that -also appear as blank spaces).

      4. string_cleaning

        Ensures string does not contain an internal termination character (\0) and -removes it if present, then removes blank spaces.

      5. prep_string

        Performs string_cleaning followed by string truncation/padding to the necessary -length.

      6. equal_within_tolerance

        Floating-point/double-precision equality within a provided tolerance (default is -f_eps, defined in sac_format.hpp).

  • 3.4. Testing

    utests.cpp contains the unit- and integration-tests, using Catch2. Test coverage +also appear as blank spaces).

  • string_cleaning

    Ensures string does not contain an internal termination character (\0) and +removes it if present, then removes blank spaces.

  • prep_string

    Performs string_cleaning followed by string truncation/padding to the necessary +length.

  • equal_within_tolerance

    Floating-point/double-precision equality within a provided tolerance (default is +f_eps, defined in sac_format.hpp).

  • 3.4. Testing

    utests.cpp contains the unit- and integration-tests, using Catch2. Test coverage details are visible on CodeCov.io and Codacy.com. All tests can be locally-run -to ensure full functionality and compliance.

    3.4.1. Errors only

    By default utests prints out a pass summary, without details unless an error is -encountered.

    3.4.2. Full output

    By passing the --success flag (utests --success) you can see the full results of -all tests.

    3.4.3. Compact output

    The full output is verbose, using the compact reporter will condense the test -results (utests --reporter=compact --success).

    3.4.4. Additional options

    To see additional options, run utests -?.

    3.5. Benchmarking

    benchmark.cpp contains the benchmarks. Running it locally will provide +to ensure full functionality and compliance.

    3.4.1. Errors only

    By default utests prints out a pass summary, without details unless an error is +encountered.

    3.4.2. Full output

    By passing the --success flag (utests --success) you can see the full results of +all tests.

    3.4.3. Compact output

    The full output is verbose, using the compact reporter will condense the test +results (utests --reporter=compact --success).

    3.4.4. Additional options

    To see additional options, run utests -?.

    3.4.5. Using ctest

    If you have CMake install, you can run the tests using ctest.

    3.5. Benchmarking

    benchmark.cpp contains the benchmarks. Running it locally will provide information on how long each function takes; benchmarks start with the low-level -I/O function and build up to Trace reading, writing, and equality comparison.

    To view available optional flags, run becnhmark -?.

    3.6. Source File List

    3.6.1. Core

    The two core files are split in the standard interface (hpp)/implementation -(cpp) format.

    1. sac_format.hpp

      Interface—function declarations and constants.

    2. sac_format.cpp

      Implementation—function details.

    3.6.2. Testing and Benchmarking

    1. util.hpp

      Utility functions and constants exclusive to testing and benchmarking. Not -split into interface/implementation.

    2. utests.cpp
    3. benchmark.cpp

    3.6.3. Example programs

    1. list_sac.cpp

    3.7. Dependencies

    3.7.1. Automatic (CMake)

    1. Xoshiro-cpp v1.12.0 (testing and benchmarking)
    2. Catch2 v3.4.0 (testing and benchmarking)

    3.8. SAC-file format

    The official and up-to-date documentation for the SAC-file format is available +I/O function and build up to Trace reading, writing, and equality comparison.

    To view available optional flags, run becnhmark -?.

    3.6. Source File List

    3.6.1. Core

    The two core files are split in the standard interface (hpp)/implementation +(cpp) format.

    1. sac_format.hpp

      Interface—function declarations and constants.

    2. sac_format.cpp

      Implementation—function details.

    3.6.2. Testing and Benchmarking

    1. util.hpp

      Utility functions and constants exclusive to testing and benchmarking. Not +split into interface/implementation.

    2. utests.cpp
    3. benchmark.cpp

    3.6.3. Example programs

    1. list_sac.cpp

    3.7. Dependencies

    3.7.1. Automatic (CMake)

    1. Xoshiro-cpp v1.12.0 (testing and benchmarking)
    2. Catch2 v3.4.0 (testing and benchmarking)

    3.8. SAC-file format

    The official and up-to-date documentation for the SAC-file format is available from the EarthScope Consortium (formerly IRIS/UNAVCO) here. The following subsections constitute my notes on the format. Below is a quick guide—all credit for the creation of, and documentation for, the SAC file-format belongs -to its developers and maintainers (details here).

    3.8.1. Floating-point (39)

    32-bit (1 word, 4 bytes)

    1. depmin

      Minimum value of the dependent variable -(displacement/velocity/acceleration/volts/counts).

    2. depmen

      Mean value of the dependent variable.

    3. depmax

      Maximum value of the dependent variable.

    4. odelta

      Modified (observational) value of delta.

    5. resp(0--9)

      Instrument response parameters (poles, zeros, and a constant).

      Not used by SAC—they’re free for other purposes.

    6. stel

      Station elevation in meters above sea level (m.a.s.l).

      Not used by SAC—free for other purposes.

    7. stdp

      Station depth in meters below surface (borehole/buried vault).

      Not used by SAC—free for other purposes.

    8. evel

      Event elevation m.a.s.l.

      Not used by SAC—free for other purposes.

    9. evdp

      Event depth in kilometers (previously meters) below surface.

    10. mag

      Event magnitude.

    11. user(0--9)

      Storage for user-defined values.

    12. dist

      Station–Event distance in kilometers.

    13. az

      Azimuth \(\mathrm{\left(Event \to Station\right)}\), decimal degrees from North.

    14. baz

      Back-azimuth \(\mathrm{\left(Station \to Event\right)}\), decimal degrees from -North.

    15. gcarc

      Station–Event great circle arc-length, decimal degrees.

    16. cmpaz

      Instrument measurement azimuth, decimal degrees from North.
      ValueDirection
      North
      90°East
      180°South
      270°West
      Other1/2/3

    17. cmpinc

      Instrument measurement incident angle, decimal degrees from upward vertical +to its developers and maintainers (details here).

      3.8.1. Floating-point (39)

      32-bit (1 word, 4 bytes)

      1. depmin

        Minimum value of the dependent variable +(displacement/velocity/acceleration/volts/counts).

      2. depmen

        Mean value of the dependent variable.

      3. depmax

        Maximum value of the dependent variable.

      4. odelta

        Modified (observational) value of delta.

      5. resp(0--9)

        Instrument response parameters (poles, zeros, and a constant).

        Not used by SAC—they’re free for other purposes.

      6. stel

        Station elevation in meters above sea level (m.a.s.l).

        Not used by SAC—free for other purposes.

      7. stdp

        Station depth in meters below surface (borehole/buried vault).

        Not used by SAC—free for other purposes.

      8. evel

        Event elevation m.a.s.l.

        Not used by SAC—free for other purposes.

      9. evdp

        Event depth in kilometers (previously meters) below surface.

      10. mag

        Event magnitude.

      11. user(0--9)

        Storage for user-defined values.

      12. dist

        Station–Event distance in kilometers.

      13. az

        Azimuth \(\mathrm{\left(Event \to Station\right)}\), decimal degrees from North.

      14. baz

        Back-azimuth \(\mathrm{\left(Station \to Event\right)}\), decimal degrees from +North.

      15. gcarc

        Station–Event great circle arc-length, decimal degrees.

      16. cmpaz

        Instrument measurement azimuth, decimal degrees from North.
        ValueDirection
        North
        90°East
        180°South
        270°West
        Other1/2/3

      17. cmpinc

        Instrument measurement incident angle, decimal degrees from upward vertical (incident 0° = dip -90°).
        ValueDirection
        Up
        90°Horizontal
        180°Down
        270°Horizontal

        NOTE: SEED/MINISEED use dip angle, decimal degrees down from horizontal (dip -0° = incident 90°).

      18. xminimum

        Spectral-only equivalent of depmin (\(f_{0}\) or \(\omega_{0}\)).

      19. xmaximum

        Spectral-only equivalent of depmax (\(f_{max}\) or \(\omega_{max}\)).

      20. yminimum

        Spectral-only equivalent of b.

      21. ymaximum

        Spectral-only equivalent of e.

      3.8.2. Double (22)

      64-bit (2 words, 8 bytes)

      NOTE: in the header section these are floats—they’re doubles in the footer +0° = incident 90°).

    18. xminimum

      Spectral-only equivalent of depmin (\(f_{0}\) or \(\omega_{0}\)).

    19. xmaximum

      Spectral-only equivalent of depmax (\(f_{max}\) or \(\omega_{max}\)).

    20. yminimum

      Spectral-only equivalent of b.

    21. ymaximum

      Spectral-only equivalent of e.

    3.8.2. Double (22)

    64-bit (2 words, 8 bytes)

    NOTE: in the header section these are floats—they’re doubles in the footer section of v7 SAC-files. In memory they’re stored as doubles regardless of the -SAC-file version.

    1. delta

      Increment between evenly spaced samples (\(\Delta t\) for timeseries, \(\Delta f\) -or \(\Delta\omega\) for spectra).

    2. b

      First value (begin) of independent variable (\(t_{0}\)).

    3. e

      Final value (end) of independent variable (\(t_{max}\)).

    4. o

      Event origin time, in seconds relative to the reference time.

    5. a

      Event first arrival time, in seconds relative to the reference time.

    6. t(0--9)

      User defined time values, in seconds relative to the reference time.

    7. f

      Event end (fini) time, in seconds relative to the reference time.

    8. stla

      Station latitude in decimal degrees, N/S–positive/negative.

      sac-format automatically enforces \(\mathrm{stla}\in[-90, 90]\).

    9. stlo

      Station longitude in decimal degrees, E/W–positive/negative.

      sac-format automatically enforces \(\mathrm{stlo}\in[-180, 180]\).

    10. evla

      Event latitude in decimal degrees, N/S–positive/negative.

      sac-format automatically enforces \(\mathrm{evla}\in[-90, 90]\).

    11. evlo

      Event longitude in decimal degrees, E/W–positive/negative.

      sac-format automatically enforces \(\mathrm{evlo}\in[-180, 180]\).

    12. sb

      Original (saved) b value.

    13. sdelta

      Original (saved) delta value.

    3.8.3. Integer (26)

    32-bit (1 word, 4 bytes)

    1. nzyear

      Reference time GMT year.

    2. nzjday

      Reference time GMT day-of-year (often called Julian Date) (1–366).

    3. nzhour

      Reference time GMT hour (00–23).

    4. nzmin

      Reference time GMT minute (0–59).

    5. nzsec

      Reference time GMT second (0–59).

    6. nzmsec

      Reference time GMT Millisecond (0–999).

    7. nvhdr

      SAC-file version.
      VersionDescription
      v7Footer (2020+, sac 102.0+)
      v6No footer (pre-2020, sac 101.6a-)

    8. norid

      Origin ID.

    9. nevid

      Event ID.

    10. npts

      Number of points in data.

    11. nsnpts

      Original (saved) npts.

    12. nwfid

      Waveform ID.

    13. nxsize

      Spectral-only equivalent of npts (length of spectrum).

    14. nysize

      Spectral-only, width of spectrum.

    15. iftype

      File type.
      ValueTypeDescription
      01ITIMETime-series
      02IRLIMSpectral (real/imaginary)
      03IAMPHSpectral (amplitude/phase)
      04IXYGeneral XY file
      ??IXYZ∗General XYZ file

      ∗Value not listed in the standard.

    16. idep

      Dependent variable type.
      ValueTypeDescription
      05IUNKNUnknown
      06IDISPDisplacement (nm)
      07IVELVelocity \(\mathrm{\left(\frac{nm}{s}\right)}\)
      08IACCAcceleration \(\mathrm{\left(\frac{nm}{s^{2}}\right)}\)
      50IVOLTSVelocity (volts)

    17. iztype

      Reference time equivalent.
      ValueTypeDescription
      05IUNKNUnknown
      09IBRecording start time
      10IDAYMidnight reference GMT day
      11IOEvent origin time
      12IAFirst arrival time
      13–22IT(0–9)User defined time (t) pick

    18. iinst

      Recording instrument type.

      Not used by SAC—free for other purposes.

    19. istreg

      Station geographic region.

      Not used by SAC—free for other purposes.

    20. ievreg

      Event geographic region.

      Not used by SAC—free for other purposes.

    21. ievtyp

      Event type.
      ValueTypeDescription
      05IUNKNUnknown
      11IOOther source of known origin
      37INUCLNuclear
      38IPRENNuclear pre-shot
      39IPOSTNNuclear post-shot
      40IQUAKEEarthquake
      41IPREQForeshock
      42IPOSTQAftershock
      43ICHEMChemical explosion
      44IOTHEROther
      72IQBQuarry/mine blast—confirmed by quarry/mine
      73IQB1Quarry/mine blast—designed shot info-ripple fired
      74IQB2Quarry/mine blast—observed shot info-ripple fired
      75IQBXQuarry/mine blast—single shot
      76IQMTQuarry/mining induced events—tremor and rockbursts
      77IEQEarthquake
      78IEQ1Earthquake in a swarm or in an aftershock sequence
      79IEQ2Felt earthquake
      80IMEMarine explosion
      81IEXOther explosion
      82INUNuclear explosion
      83INCNuclear cavity collapse
      85ILLocal event of unknown origin
      86IRRegion event of unknown origin
      87ITTeleseismic event of unknown origin
      88IUUndetermined/conflicting information

    22. iqual

      Quality of data.
      ValueTypeDescription
      44IOTHEROther
      45IGOODGood
      46IGLCHGlitches
      47IDROPDropouts
      48ILOWSNLow signal-to-noise ratio

      Not used by SAC—free for other purposes.

    23. isynth

      Synthetic data flag.
      ValueTypeDescription
      49IRLDATAReal data
      XXSynthetic

      ∗Values and types not listed in the standard.

    24. imagtyp

      Magnitude type.
      ValueTypeDescription
      52IMBBody-wave magnitude (\(M_{b}\))
      53IMSSurface-wave magnitude (\(M_{s}\))
      54IMLLocal magnitude (\(M_{l}\))
      55IMWMoment magnitude (\(M_{w}\))
      56IMDDuration magnitude (\(M_{d}\))
      57IMXUser-defined magnitude (\(M_{x}\))

    25. imagsrc

      Source of magnitude information.
      ValueTypeDescription
      58INEICNational Earthquake Information Center
      61IPDEPreliminary Determination of Epicenter
      62IISCInternation Seismological Centre
      63IREBReviewed Event Bulletin
      64IUSGSU.S. Geological Survey
      65IBRKUC Berkeley
      66ICALTECHCalifornia Institute of Technology
      67ILLNLLawrence Livermore National Laboratory
      68IEVLOCEvent location (computer program)
      69IJSOPJoint Seismic Observation Program
      70IUSERThe user
      71IUNKNOWNUnknown

    26. ibody

      Body/spheroid definition used to calculate distances.
      ValueTypeNameSemi-major axis (a [m])Inverse Flattening (f)
      -12345UNDEFEarth (Historic)6378160.00.00335293
      98ISUNSun696000000.08.189e-6
      99IMERCURYMercury2439700.00.0
      100IVENUSVenus6051800.00.0
      101IEARTHEarth (WGS84)6378137.00.0033528106647474805
      102IMOONMoon1737400.00.0
      103IMARSMars3396190.00.005886007555525457

    3.8.4. Boolean (4)

    32-bit (1 word, 4 bytes) in-file/8-bit (1 byte) in-memory

    1. leven

      REQUIRED

      Evenly-spaced data flag.

      If true, then data is evenly spaced.

    2. lpspol

      Station polarity flag.

      If true, then station has positive-polarity—it follows the left-hand -convention (for example, North-East-Up [NEZ]).

    3. lovrok

      File overwrite flag.

      If true, then it’s okay to overwrite the file.

    4. lcalda

      Calculate geometry flag.

      If true, then calculate dist, az, baz, and gcarc from stla, stlo, -evla, and evlo.

    3.8.5. String (23)

    32/64-bit (2/4 words, 8/16 bytes, 8/16 characters)

    1. kstnm

      Station name.

    2. kevnm

      Event name.

      ∗This is the only four word (16 character) string.

    3. khole

      Nuclear—hole identifier.

      Other—Location identifier (LOCID).

    4. ko

      Text for o.

    5. ka

      Text for a.

    6. kt(0--9)

      Text for t(0--9).

    7. kf

      Text for f.

    8. kuser(0--2)

      Text for the first three of user(0--9).

    9. kdatrd

      Date the data was read onto a computer.

    10. kinst

      Text for iinst.

    3.8.6. Data (2)

    32-bit (2 words, 8 bytes) in-file/64-bit (4 words, 16 bytes) in-memory

    Stored as floating-point (32-bit) values in SAC-files; stored as -double-precision in memory.

    1. data1

      The first data vector---always present in a SAC-file and begins at word 158.

    2. data2

      The second data vector---conditionally present and begins after data1.

      Required if leven is false, or if iftype is spectral/XY/XYZ.

    4. Notes

    4.1. Why C++20 and not C++23

    Compiler restrictions—C++23 support requires GCC-13+ and Clang-16+. Many +SAC-file version.

    1. delta

      Increment between evenly spaced samples (\(\Delta t\) for timeseries, \(\Delta f\) +or \(\Delta\omega\) for spectra).

    2. b

      First value (begin) of independent variable (\(t_{0}\)).

    3. e

      Final value (end) of independent variable (\(t_{max}\)).

    4. o

      Event origin time, in seconds relative to the reference time.

    5. a

      Event first arrival time, in seconds relative to the reference time.

    6. t(0--9)

      User defined time values, in seconds relative to the reference time.

    7. f

      Event end (fini) time, in seconds relative to the reference time.

    8. stla

      Station latitude in decimal degrees, N/S–positive/negative.

      sac-format automatically enforces \(\mathrm{stla}\in[-90, 90]\).

    9. stlo

      Station longitude in decimal degrees, E/W–positive/negative.

      sac-format automatically enforces \(\mathrm{stlo}\in[-180, 180]\).

    10. evla

      Event latitude in decimal degrees, N/S–positive/negative.

      sac-format automatically enforces \(\mathrm{evla}\in[-90, 90]\).

    11. evlo

      Event longitude in decimal degrees, E/W–positive/negative.

      sac-format automatically enforces \(\mathrm{evlo}\in[-180, 180]\).

    12. sb

      Original (saved) b value.

    13. sdelta

      Original (saved) delta value.

    3.8.3. Integer (26)

    32-bit (1 word, 4 bytes)

    1. nzyear

      Reference time GMT year.

    2. nzjday

      Reference time GMT day-of-year (often called Julian Date) (1–366).

    3. nzhour

      Reference time GMT hour (00–23).

    4. nzmin

      Reference time GMT minute (0–59).

    5. nzsec

      Reference time GMT second (0–59).

    6. nzmsec

      Reference time GMT Millisecond (0–999).

    7. nvhdr

      SAC-file version.
      VersionDescription
      v7Footer (2020+, sac 102.0+)
      v6No footer (pre-2020, sac 101.6a-)

    8. norid

      Origin ID.

    9. nevid

      Event ID.

    10. npts

      Number of points in data.

    11. nsnpts

      Original (saved) npts.

    12. nwfid

      Waveform ID.

    13. nxsize

      Spectral-only equivalent of npts (length of spectrum).

    14. nysize

      Spectral-only, width of spectrum.

    15. iftype

      File type.
      ValueTypeDescription
      01ITIMETime-series
      02IRLIMSpectral (real/imaginary)
      03IAMPHSpectral (amplitude/phase)
      04IXYGeneral XY file
      ??IXYZ∗General XYZ file

      ∗Value not listed in the standard.

    16. idep

      Dependent variable type.
      ValueTypeDescription
      05IUNKNUnknown
      06IDISPDisplacement (nm)
      07IVELVelocity \(\mathrm{\left(\frac{nm}{s}\right)}\)
      08IACCAcceleration \(\mathrm{\left(\frac{nm}{s^{2}}\right)}\)
      50IVOLTSVelocity (volts)

    17. iztype

      Reference time equivalent.
      ValueTypeDescription
      05IUNKNUnknown
      09IBRecording start time
      10IDAYMidnight reference GMT day
      11IOEvent origin time
      12IAFirst arrival time
      13–22IT(0–9)User defined time (t) pick

    18. iinst

      Recording instrument type.

      Not used by SAC—free for other purposes.

    19. istreg

      Station geographic region.

      Not used by SAC—free for other purposes.

    20. ievreg

      Event geographic region.

      Not used by SAC—free for other purposes.

    21. ievtyp

      Event type.
      ValueTypeDescription
      05IUNKNUnknown
      11IOOther source of known origin
      37INUCLNuclear
      38IPRENNuclear pre-shot
      39IPOSTNNuclear post-shot
      40IQUAKEEarthquake
      41IPREQForeshock
      42IPOSTQAftershock
      43ICHEMChemical explosion
      44IOTHEROther
      72IQBQuarry/mine blast—confirmed by quarry/mine
      73IQB1Quarry/mine blast—designed shot info-ripple fired
      74IQB2Quarry/mine blast—observed shot info-ripple fired
      75IQBXQuarry/mine blast—single shot
      76IQMTQuarry/mining induced events—tremor and rockbursts
      77IEQEarthquake
      78IEQ1Earthquake in a swarm or in an aftershock sequence
      79IEQ2Felt earthquake
      80IMEMarine explosion
      81IEXOther explosion
      82INUNuclear explosion
      83INCNuclear cavity collapse
      85ILLocal event of unknown origin
      86IRRegion event of unknown origin
      87ITTeleseismic event of unknown origin
      88IUUndetermined/conflicting information

    22. iqual

      Quality of data.
      ValueTypeDescription
      44IOTHEROther
      45IGOODGood
      46IGLCHGlitches
      47IDROPDropouts
      48ILOWSNLow signal-to-noise ratio

      Not used by SAC—free for other purposes.

    23. isynth

      Synthetic data flag.
      ValueTypeDescription
      49IRLDATAReal data
      XXSynthetic

      ∗Values and types not listed in the standard.

    24. imagtyp

      Magnitude type.
      ValueTypeDescription
      52IMBBody-wave magnitude (\(M_{b}\))
      53IMSSurface-wave magnitude (\(M_{s}\))
      54IMLLocal magnitude (\(M_{l}\))
      55IMWMoment magnitude (\(M_{w}\))
      56IMDDuration magnitude (\(M_{d}\))
      57IMXUser-defined magnitude (\(M_{x}\))

    25. imagsrc

      Source of magnitude information.
      ValueTypeDescription
      58INEICNational Earthquake Information Center
      61IPDEPreliminary Determination of Epicenter
      62IISCInternation Seismological Centre
      63IREBReviewed Event Bulletin
      64IUSGSU.S. Geological Survey
      65IBRKUC Berkeley
      66ICALTECHCalifornia Institute of Technology
      67ILLNLLawrence Livermore National Laboratory
      68IEVLOCEvent location (computer program)
      69IJSOPJoint Seismic Observation Program
      70IUSERThe user
      71IUNKNOWNUnknown

    26. ibody

      Body/spheroid definition used to calculate distances.
      ValueTypeNameSemi-major axis (a [m])Inverse Flattening (f)
      -12345UNDEFEarth (Historic)6378160.00.00335293
      98ISUNSun696000000.08.189e-6
      99IMERCURYMercury2439700.00.0
      100IVENUSVenus6051800.00.0
      101IEARTHEarth (WGS84)6378137.00.0033528106647474805
      102IMOONMoon1737400.00.0
      103IMARSMars3396190.00.005886007555525457

    3.8.4. Boolean (4)

    32-bit (1 word, 4 bytes) in-file/8-bit (1 byte) in-memory

    1. leven

      REQUIRED

      Evenly-spaced data flag.

      If true, then data is evenly spaced.

    2. lpspol

      Station polarity flag.

      If true, then station has positive-polarity—it follows the left-hand +convention (for example, North-East-Up [NEZ]).

    3. lovrok

      File overwrite flag.

      If true, then it’s okay to overwrite the file.

    4. lcalda

      Calculate geometry flag.

      If true, then calculate dist, az, baz, and gcarc from stla, stlo, +evla, and evlo.

    3.8.5. String (23)

    32/64-bit (2/4 words, 8/16 bytes, 8/16 characters)

    1. kstnm

      Station name.

    2. kevnm

      Event name.

      ∗This is the only four word (16 character) string.

    3. khole

      Nuclear—hole identifier.

      Other—Location identifier (LOCID).

    4. ko

      Text for o.

    5. ka

      Text for a.

    6. kt(0--9)

      Text for t(0--9).

    7. kf

      Text for f.

    8. kuser(0--2)

      Text for the first three of user(0--9).

    9. kdatrd

      Date the data was read onto a computer.

    10. kinst

      Text for iinst.

    3.8.6. Data (2)

    32-bit (2 words, 8 bytes) in-file/64-bit (4 words, 16 bytes) in-memory

    Stored as floating-point (32-bit) values in SAC-files; stored as +double-precision in memory.

    1. data1

      The first data vector---always present in a SAC-file and begins at word 158.

    2. data2

      The second data vector---conditionally present and begins after data1.

      Required if leven is false, or if iftype is spectral/XY/XYZ.

    4. Notes

    4.1. Why C++20 and not C++23

    Compiler restrictions—C++23 support requires GCC-13+ and Clang-16+. Many systems, still use GCC-12 and Clang-15—which has near complete support for C++20.

    sac-format strives for accessibility, modernity, safety, and speed—C++20 provides the best fit.

    Author: Alexander R. Blanchette

    Validate

    \ No newline at end of file diff --git a/docs/sac-format_manual.pdf b/docs/sac-format_manual.pdf index 4903549..decbdc4 100644 Binary files a/docs/sac-format_manual.pdf and b/docs/sac-format_manual.pdf differ diff --git a/src/docs/index.org b/src/docs/index.org index 887493e..f1e8f38 100644 --- a/src/docs/index.org +++ b/src/docs/index.org @@ -84,10 +84,14 @@ running. *** Small -sac-format is *small*---in total (header + implementation--excluding comments) -it's fewer than 2000 lines of code. Small size opens the door to using on any +sac-format is *small*---in total (header + implementation--excluding comments) the +library is under 2100\ast{} lines of code. Small size opens the door to using on any sort of hardware (old or new) and makes it easy to expand upon. +\ast{} This value includes only the library, excluding all testing/benchmarking +and example codes. Including =utests.cpp=, =benchmark.cpp=, =util.hpp=, the example +program (=list_sac=), and sac-format totals just over 5100 lines of code. + *** Documented sac-format is extensively *documented*---both online and in the code. Nothing's @@ -272,6 +276,100 @@ Every [[SAC-file format][SAC variable]] is accessed via getters and setters of t - =trace.evla(32.89)= - =trace.mag(3.21)= +**** Setter rules + +Most of the setters are only constrained by the parameter type +(single-precision, double-precision, boolean, etc.). *Some* setters are +constrained by additional rules. + +***** Required for sanity + +Rules here are required because the sac-format library assumes them (not +strictly required by the SAC format standard). For instance, the geometric +functions assume certain bounds on latitudes and longitudes. sac-format +automatically imposes these rules. + +****** =stla(input)= + +Limited to $[-90, 90]$ degrees, input that is outside that range is reduced +using circular symmetry. + +****** =stlo(input)= + +Limited to $[-180, 180]$ degrees, input that is outside that range is reduced +using circular symmetry. + +****** =evla(input)= + +Limited to $[-90, 90]$ degrees, input that is outside that range is reduced +using circular symmetry. + +****** =evlo(input)= + +Limited to $[-180, 180]$ degrees, input that is outside that range is reduced +using circular symmetry. + +***** Require for safety + +Rules here are required by the SAC format standard. sac-format automatically +imposes these rules to prevent the creation of corrupt sac-files. + +****** =npts(input)= + +Because =npts= defines the size of the data vectors, changing this value will +change the size of =data1= and =data2=\ast{}. Increasing npts resizes the vectors +([[https://en.cppreference.com/w/cpp/container/vector/resize][std::vector::resize]]) by placing zeros at the *end* of the vectors. Reducing npts +resizes the vectors down to the *first npts* values. + +Therefore, care must be taken to maintain separate copies of =data1= and +=data2=\ast{} if you plan to manipulate the original data *after* resizing. + +\ast{} data2 has =npts= only if it is legal, otherwise it is of size 0. + +****** =leven(input)= + +Changing the value of =leven= potentially changes the legality of =data2=, it also +potentially affects the value of =iftype=. + +If iftype$>1$, then leven must be ~true~ (evenly sampled data). Therefore, if +leven is made ~false~ in this scenario (unevenly sampled data) then iftype becomes +unset\ast{}. + +If changing leven makes data2 legal\ast{}\ast{}, then data2 is resized to have +=npts= zeros. + +\ast{} The SAC format defines the unset values for all data-types. For integers +(like iftype) it is the integer value ~-12345~. + +\ast{}\ast{} If data2 was already legal, then it is unaffected. + +****** =iftype(input)= + +Changing the value of =iftype= poentially changes the legality of =data2=, it also potentially affects the value of =leven=. + +If leven is =false=, then iftype must be either 1 or unset. Therefore, changing +iftype to have a value $>1$ requires that leven becomes =true= (evenly sampled +data). + +If changing iftype makes data2 legal\ast{}, then data2 is resized to have =npts= zeros. + +\ast{} If data2 was already legal, then it is unaffected. + +****** =data1(input)= + +If the size of =data1= is changed, then =npts= must change to reflect the new size. +If =data2= is legal, this adjusts its size to match as well. + +****** =data2(input)= + +If the size of =data2= is changed to be larger than 0 and it is illegal, it is +made legal by setting =iftype(2)= (spectral-data). + +When the size of data2 changes, =npts= is updated to the new size and =data1= is +resized to match. + +If =data2= is made illegal, its size is reduced to 0 while =npts= and =data1= are +unaffected. *** Internal Structure The SAC-trace stores the data internally in a series of pre-allocated @@ -576,6 +674,11 @@ results (=utests --reporter=compact --success=). To see additional options, run =utests -?=. +*** Using ctest + +If you have CMake install, you can run the tests using =ctest=. + + ** Benchmarking =benchmark.cpp= contains the benchmarks. Running it locally will provide diff --git a/src/sac_format.cpp b/src/sac_format.cpp index 2250663..024a812 100644 --- a/src/sac_format.cpp +++ b/src/sac_format.cpp @@ -930,7 +930,11 @@ void Trace::nevid(const int input) noexcept { ints[sac_map.at(name::nevid)] = input; } void Trace::npts(const int input) noexcept { - ints[sac_map.at(name::npts)] = input; + if ((input >= 0) || (input == unset_int)) { + ints[sac_map.at(name::npts)] = input; + const size_t size{static_cast(input >= 0 ? input : 0)}; + resize_data(size); + } } void Trace::nsnpts(const int input) noexcept { ints[sac_map.at(name::nsnpts)] = input; @@ -946,6 +950,12 @@ void Trace::nysize(const int input) noexcept { } void Trace::iftype(const int input) noexcept { ints[sac_map.at(name::iftype)] = input; + const int size{npts() >= 0 ? npts() : 0}; + // Uneven 2D data not supported as not in specification + if ((input > 1) && !leven()) { + leven(true); + } + resize_data2(size); } void Trace::idep(const int input) noexcept { ints[sac_map.at(name::idep)] = input; @@ -983,6 +993,12 @@ void Trace::ibody(const int input) noexcept { // Bools void Trace::leven(const bool input) noexcept { bools[sac_map.at(name::leven)] = input; + const int size{npts() >= 0 ? npts() : 0}; + // Uneven 2D data not supported since not in specification + if (!input && (iftype() > 1)) { + iftype(unset_int); + } + resize_data2(size); } void Trace::lpspol(const bool input) noexcept { bools[sac_map.at(name::lpspol)] = input; @@ -1066,9 +1082,59 @@ void Trace::kinst(const std::string &input) noexcept { // Data void Trace::data1(const std::vector &input) noexcept { data[sac_map.at(name::data1)] = input; + // Propagate change as needed + size_t size{data1().size()}; + size = (((size == 0) && (npts() == unset_int)) ? unset_int : size); + if (static_cast(size) != npts()) { + npts(static_cast(size)); + } } void Trace::data2(const std::vector &input) noexcept { data[sac_map.at(name::data2)] = input; + // Proagate change as needed + size_t size{data2().size()}; + size = (((size == 0) && (npts() == unset_int)) ? unset_int : size); + // Need to make sure this is legal + // If positive size and not-legal, make spectral + if (size > 0) { + // If not legal, make spectral + if (leven() && (iftype() <= 1)) { + iftype(2); + } + // If legal and different from npts, update npts + if ((!leven() || (iftype() > 1)) && (static_cast(size) != npts())) { + npts(static_cast(size)); + } + } +} + +void Trace::resize_data1(const size_t size) noexcept { + if (size != data1().size()) { + std::vector new_data1{data1()}; + new_data1.resize(size, 0.0); + data1(new_data1); + } +} + +void Trace::resize_data2(const size_t size) noexcept { + // Data2 is legal + if (!leven() || (iftype() > 1)) { + if (size != data2().size()) { + std::vector new_data2{data2()}; + new_data2.resize(size, 0.0); + data2(new_data2); + } + } else { + if (!data2().empty()) { + std::vector new_data2{}; + data2(new_data2); + } + } +} + +void Trace::resize_data(const size_t size) noexcept { + resize_data1(size); + resize_data2(size); } //------------------------------------------------------------------------------ // Read diff --git a/src/sac_format.hpp b/src/sac_format.hpp index f63849c..830c68b 100644 --- a/src/sac_format.hpp +++ b/src/sac_format.hpp @@ -737,6 +737,9 @@ class Trace { void calc_az() noexcept; void calc_baz() noexcept; bool geometry_set() const noexcept; + void resize_data1(size_t size) noexcept; + void resize_data2(size_t size) noexcept; + void resize_data(size_t size) noexcept; // Objects // cppcheck-suppress unusedStructMember std::array floats{}; diff --git a/src/utests.cpp b/src/utests.cpp index 941706a..da73b89 100644 --- a/src/utests.cpp +++ b/src/utests.cpp @@ -870,6 +870,297 @@ TEST_CASE("Trace Equality") { } } +void quick_io_check(fs::path tmp_file, const Trace &trace) { + // Write/load and check + trace.write(tmp_file); + Trace in = Trace(tmp_file); + fs::remove(tmp_file); + REQUIRE(in == trace); +} + +TEST_CASE("Linked Headers") { + const fs::path tmp_dir{fs::temp_directory_path()}; + const fs::path tmp_file{tmp_dir / "linked_parameter_test.sac"}; + SECTION("LEven") { + Trace trace{}; + // Even non-2D data + trace.leven(true); + trace.iftype(1); + trace.npts(10); + trace.nvhdr(7); + REQUIRE(trace.leven() == true); + REQUIRE(trace.iftype() == 1); + REQUIRE(trace.data2().size() == 0); + quick_io_check(tmp_file, trace); + // Uneven data + trace.leven(false); + REQUIRE(trace.iftype() == 1); + quick_io_check(tmp_file, trace); + // Return to even data + trace.leven(true); + REQUIRE(trace.iftype() == 1); + REQUIRE(trace.data2().size() == 0); + quick_io_check(tmp_file, trace); + // Spectral even + trace.iftype(2); + REQUIRE(trace.iftype() == 2); + REQUIRE(trace.leven() == true); + REQUIRE(trace.data2().size() == static_cast(trace.npts())); + quick_io_check(tmp_file, trace); + // Uneven + trace.leven(false); + REQUIRE(trace.iftype() == unset_int); + REQUIRE(trace.data2().size() == static_cast(trace.npts())); + quick_io_check(tmp_file, trace); + // Return to even + trace.leven(true); + REQUIRE(trace.iftype() == unset_int); + REQUIRE(trace.data2().size() == 0); + quick_io_check(tmp_file, trace); + } + SECTION("IFType") { + Trace trace{}; + // Even non-2D data + trace.leven(true); + trace.iftype(1); + trace.npts(10); + trace.nvhdr(7); + REQUIRE(trace.leven() == true); + REQUIRE(trace.iftype() == 1); + REQUIRE(trace.data2().size() == 0); + quick_io_check(tmp_file, trace); + // Spectral data + trace.iftype(2); + REQUIRE(trace.leven() == true); + REQUIRE(trace.data2().size() == static_cast(trace.npts())); + quick_io_check(tmp_file, trace); + // Return to even non-2D data + trace.iftype(1); + REQUIRE(trace.leven() == true); + REQUIRE(trace.data2().size() == 0); + quick_io_check(tmp_file, trace); + } + SECTION("Npts") { + SECTION("Without Data2") { + Trace trace{}; + // Even non-2D data + trace.leven(true); + trace.iftype(1); + trace.npts(0); + trace.nvhdr(7); + REQUIRE(trace.npts() == 0); + REQUIRE(trace.data1().size() == static_cast(trace.npts())); + REQUIRE(trace.data2().size() == 0); + quick_io_check(tmp_file, trace); + SECTION("Npts 10") { + trace.npts(10); + REQUIRE(trace.npts() == 10); + REQUIRE(trace.data1().size() == static_cast(trace.npts())); + REQUIRE(trace.data2().size() == 0); + quick_io_check(tmp_file, trace); + } + SECTION("Grow 10, shrink 5") { + trace.npts(10); + trace.npts(5); + REQUIRE(trace.npts() == 5); + REQUIRE(trace.data1().size() == static_cast(trace.npts())); + REQUIRE(trace.data2().size() == 0); + quick_io_check(tmp_file, trace); + } + } + SECTION("With Data2") { + SECTION("Uneven timeseries") { + Trace trace{}; + trace.leven(false); + trace.iftype(1); + trace.npts(0); + trace.nvhdr(7); + REQUIRE(trace.npts() == 0); + REQUIRE(trace.data1().size() == static_cast(trace.npts())); + REQUIRE(trace.data2().size() == static_cast(trace.npts())); + quick_io_check(tmp_file, trace); + SECTION("Npts 10") { + trace.npts(10); + REQUIRE(trace.npts() == 10); + REQUIRE(trace.data1().size() == static_cast(trace.npts())); + REQUIRE(trace.data2().size() == static_cast(trace.npts())); + quick_io_check(tmp_file, trace); + } + SECTION("Grow 10, shrink 5") { + trace.npts(10); + trace.npts(5); + REQUIRE(trace.npts() == 5); + REQUIRE(trace.data1().size() == static_cast(trace.npts())); + REQUIRE(trace.data2().size() == static_cast(trace.npts())); + quick_io_check(tmp_file, trace); + } + } + SECTION("Even Spectral") { + Trace trace{}; + trace.leven(true); + trace.iftype(2); + trace.npts(0); + trace.nvhdr(7); + REQUIRE(trace.npts() == 0); + REQUIRE(trace.data1().size() == static_cast(trace.npts())); + REQUIRE(trace.data2().size() == static_cast(trace.npts())); + quick_io_check(tmp_file, trace); + SECTION("Npts 10") { + trace.npts(10); + REQUIRE(trace.npts() == 10); + REQUIRE(trace.data1().size() == static_cast(trace.npts())); + REQUIRE(trace.data2().size() == static_cast(trace.npts())); + quick_io_check(tmp_file, trace); + } + SECTION("Grow 10, shrink 5") { + trace.npts(10); + trace.npts(5); + REQUIRE(trace.npts() == 5); + REQUIRE(trace.data1().size() == static_cast(trace.npts())); + REQUIRE(trace.data2().size() == static_cast(trace.npts())); + quick_io_check(tmp_file, trace); + } + } + } + } + SECTION("Data1") { + SECTION("Even timeseries") { + Trace trace{}; + trace.leven(true); + trace.iftype(1); + trace.npts(0); + trace.nvhdr(7); + REQUIRE(trace.npts() == 0); + REQUIRE(trace.data1().size() == 0); + REQUIRE(trace.data2().size() == 0); + quick_io_check(tmp_file, trace); + // Change the vector + std::vector new_data1{}; + new_data1.resize(10); + trace.data1(new_data1); + REQUIRE(trace.npts() == 10); + REQUIRE(trace.data1().size() == static_cast(trace.npts())); + REQUIRE(trace.data2().size() == 0); + quick_io_check(tmp_file, trace); + } + SECTION("Uneven timeseries") { + Trace trace{}; + trace.leven(false); + trace.iftype(1); + trace.npts(0); + trace.nvhdr(7); + REQUIRE(trace.npts() == 0); + REQUIRE(trace.data1().size() == 0); + REQUIRE(trace.data2().size() == 0); + quick_io_check(tmp_file, trace); + // Change the vector + std::vector new_data1{}; + new_data1.resize(10); + trace.data1(new_data1); + REQUIRE(trace.npts() == 10); + REQUIRE(trace.data1().size() == static_cast(trace.npts())); + REQUIRE(trace.data2().size() == static_cast(trace.npts())); + quick_io_check(tmp_file, trace); + } + SECTION("Spectral") { + Trace trace{}; + trace.leven(true); + trace.iftype(2); + trace.npts(0); + trace.nvhdr(7); + REQUIRE(trace.npts() == 0); + REQUIRE(trace.data1().size() == 0); + REQUIRE(trace.data2().size() == 0); + quick_io_check(tmp_file, trace); + // Change the vector + std::vector new_data1{}; + new_data1.resize(10); + trace.data1(new_data1); + REQUIRE(trace.npts() == 10); + REQUIRE(trace.data1().size() == static_cast(trace.npts())); + REQUIRE(trace.data2().size() == static_cast(trace.npts())); + quick_io_check(tmp_file, trace); + } + } + SECTION("Data2") { + SECTION("Even timeseries") { + Trace trace{}; + trace.leven(true); + trace.iftype(1); + trace.npts(0); + trace.nvhdr(7); + REQUIRE(trace.npts() == 0); + REQUIRE(trace.data1().size() == 0); + REQUIRE(trace.data2().size() == 0); + quick_io_check(tmp_file, trace); + // Change the vector + std::vector new_data2{}; + new_data2.resize(15); + trace.data2(new_data2); + REQUIRE(trace.npts() == 15); + REQUIRE(trace.data1().size() == static_cast(trace.npts())); + REQUIRE(trace.data2().size() == static_cast(trace.npts())); + quick_io_check(tmp_file, trace); + // Remove data2 + trace.iftype(1); + REQUIRE(trace.npts() == 15); + REQUIRE(trace.data1().size() == static_cast(trace.npts())); + REQUIRE(trace.data2().size() == 0); + quick_io_check(tmp_file, trace); + } + SECTION("Uneven timeseries") { + Trace trace{}; + trace.leven(false); + trace.iftype(1); + trace.npts(0); + trace.nvhdr(7); + REQUIRE(trace.npts() == 0); + REQUIRE(trace.data1().size() == 0); + REQUIRE(trace.data2().size() == 0); + quick_io_check(tmp_file, trace); + // Change the vector + std::vector new_data2{}; + new_data2.resize(25); + trace.data2(new_data2); + REQUIRE(trace.npts() == 25); + REQUIRE(trace.data1().size() == static_cast(trace.npts())); + REQUIRE(trace.data2().size() == static_cast(trace.npts())); + quick_io_check(tmp_file, trace); + // Remove data2 + trace.leven(true); + REQUIRE(trace.npts() == 25); + REQUIRE(trace.data1().size() == static_cast(trace.npts())); + REQUIRE(trace.data2().size() == 0); + quick_io_check(tmp_file, trace); + } + SECTION("Spectral") { + Trace trace{}; + trace.leven(true); + trace.iftype(2); + trace.npts(0); + trace.nvhdr(7); + REQUIRE(trace.npts() == 0); + REQUIRE(trace.data1().size() == 0); + REQUIRE(trace.data2().size() == 0); + quick_io_check(tmp_file, trace); + // Change the vector + std::vector new_data2{}; + new_data2.resize(12); + trace.data2(new_data2); + REQUIRE(trace.npts() == 12); + REQUIRE(trace.data1().size() == static_cast(trace.npts())); + REQUIRE(trace.data2().size() == static_cast(trace.npts())); + quick_io_check(tmp_file, trace); + // Remove data2 + trace.iftype(1); + REQUIRE(trace.npts() == 12); + REQUIRE(trace.data1().size() == static_cast(trace.npts())); + REQUIRE(trace.data2().size() == 0); + quick_io_check(tmp_file, trace); + } + } +} + TEST_CASE("Unsetting Trace") { Trace trace = gen_fake_trace(); unset_trace(&trace); @@ -1009,6 +1300,10 @@ TEST_CASE("Unsetting Trace") { REQUIRE(trace.az() == unset_double); REQUIRE(trace.baz() == unset_double); } + fs::path tmp_dir{fs::temp_directory_path()}; + fs::path tmp_file{tmp_dir / "test.SaC"}; + trace.nvhdr(7); + quick_io_check(tmp_file, trace); } TEST_CASE("Trace Read/Write") { diff --git a/todo.org b/todo.org index 1d8f267..31210ae 100644 --- a/todo.org +++ b/todo.org @@ -7,18 +7,24 @@ later. Maybe call it =Seismogram=, or for less typing =Trace=. Then it'd be =SAC::Trace= which is nice. ** DONE SacStream constructor should use std::filesystem::path not std::string -** TODO Testing :testing: +** DONE Testing :testing: *** DONE Move Unit tests from PsSp :unit: *** DONE Benchmarks :benchmark: *** DONE data2 read/write :unit: *** DONE not =equal_within_tolerance= :unit: *** DONE =legacy_write= read/write :unit: -*** TODO Corrupt sac-files +*** DONE Corrupt sac-files **** DONE Insufficient header **** DONE Insufficient Data1 **** DONE Insuficcient Data2 **** DONE Insufficient footer -**** TODO File continues past expected end +**** DONE File continues past expected end +*** DONE Linked headers +**** DONE npts +**** DONE leven +**** DONE iftype +**** DONE data1 +**** DONE data2 ** TODO New functionality :functionality: *** Incorporate Ellipticity in geometric calculations @@ -35,6 +41,17 @@ lines ([[https://en.wikipedia.org/wiki/Geographical_distance#Lambert's_formula_f *** DONE Azimuth calculation Assumes spherical earth +*** DONE Ensure NPTS updates when data1/data2 updates +This would aid the user in avoiding writing out sac-files that could then not be +read in. The issue is, let's say the user wants to include data2, I cannot +simply assume level(false) or iftype(value>1). There needs to be a mechanism +that enforces a specification change in the data2 setter. + +That also means that the leven and iftype() setters need a mechanism whereby +leven must be true for iftype(>1), and if leven is false then iftype(<=1). If +leven(false) and iftype(<1), but data2 exists, clear it. +**** DONE data1 and data2 ought to have same size if data2 exists (changing one changes the other and npts) +**** DONE when npts changes, size of data1 and data2 should change *** TODO ASCII read/write support :ascii: **** TODO Fortran style text files. ***** TODO Read in