[segyio] 01/376: Initial commit.
Jørgen Kvalsvik
jokva-guest at moszumanska.debian.org
Wed Sep 20 08:03:57 UTC 2017
This is an automated email from the git hooks/post-receive script.
jokva-guest pushed a commit to branch debian
in repository segyio.
commit 5c05b1bcd9d84da7d383ef03a2f8739a2aaff1f6
Author: Jørgen Kvalsvik <jokva at statoil.com>
Date: Wed Sep 28 13:53:46 2016 +0200
Initial commit.
---
.gitlab-ci.yml | 12 +
CMakeLists.txt | 54 ++
License.md | 157 +++++
README.md | 113 +++
cmake/matlab.cmake | 257 +++++++
cmake/python.cmake | 57 ++
cmake/test_runner.py | 48 ++
examples/CMakeLists.txt | 6 +
examples/about.py | 49 ++
examples/copy-sub-cube.py | 41 ++
examples/make-file.py | 55 ++
examples/segyview.py | 32 +
examples/write.py | 84 +++
mex/CMakeLists.txt | 40 ++
mex/Segy.m | 550 +++++++++++++++
mex/SegySampleFormat.m | 13 +
mex/SegySpec.m | 48 ++
mex/TraceField.m | 96 +++
mex/TraceSortingFormat.m | 9 +
mex/get_line.c | 41 ++
mex/segy_get_bfield_mex.c | 24 +
mex/segy_get_field_mex.c | 24 +
mex/segy_get_header_mex.c | 61 ++
mex/segy_get_ntraces_mex.c | 14 +
mex/segy_get_segy_header_mex.c | 44 ++
mex/segy_get_trace_header_mex.c | 42 ++
mex/segy_get_traces_mex.c | 76 ++
mex/segy_interpret_segycube_mex.c | 8 +
mex/segy_put_headers_mex.c | 62 ++
mex/segy_put_traces_mex.c | 69 ++
mex/segy_read_write_line_mex.c | 114 +++
mex/segyspec_mex.c | 148 ++++
mex/segyutil.c | 89 +++
mex/segyutil.h | 26 +
python/CMakeLists.txt | 2 +
python/cwrap/CMakeLists.txt | 10 +
python/cwrap/__init__.py | 5 +
python/cwrap/basecclass.py | 100 +++
python/cwrap/basecenum.py | 119 ++++
python/cwrap/basecvalue.py | 47 ++
python/cwrap/metacwrap.py | 46 ++
python/cwrap/prototype.py | 147 ++++
python/segyio/CMakeLists.txt | 11 +
python/segyio/__init__.py | 42 ++
python/segyio/binfield.py | 70 ++
python/segyio/create.py | 135 ++++
python/segyio/open.py | 74 ++
python/segyio/segy.py | 1302 +++++++++++++++++++++++++++++++++++
python/segyio/segysampleformat.py | 24 +
python/segyio/tracefield.py | 189 +++++
python/segyio/tracesortingformat.py | 13 +
src/applications/segyinfo.c | 141 ++++
src/applications/segyinspect.c | 167 +++++
src/segyio/segy.c | 1131 ++++++++++++++++++++++++++++++
src/segyio/segy.h | 393 +++++++++++
src/spec/segyspec.c | 129 ++++
src/spec/segyspec.h | 33 +
tests/CMakeLists.txt | 14 +
tests/test-data/small.sgy | Bin 0 -> 14600 bytes
tests/test-data/text.sgy | Bin 0 -> 3600 bytes
tests/test_segy.c | 609 ++++++++++++++++
tests/test_segy.py | 469 +++++++++++++
tests/test_segy_mex.m | 230 +++++++
tests/test_segyspec.c | 79 +++
tests/test_segyspec_mex.m | 88 +++
tests/test_utils.c | 128 ++++
tests/unittest.h | 43 ++
67 files changed, 8553 insertions(+)
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000..ab2bb99
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,12 @@
+before_script:
+ - source /project/res/SDP_bashrc
+ - mkdir build
+ - cd build
+
+build:
+ script:
+ - cmake -D CMAKE_C_COMPILER=/opt/rh/devtoolset-3/root/usr/bin/gcc ..
+ - make -j4
+ - export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD
+ - ctest -j4
+
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..cc1a986
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,54 @@
+cmake_minimum_required(VERSION 2.8)
+project(segyio)
+
+option(BUILD_MEX "Build matlab mex files" OFF)
+
+include(cmake/python.cmake)
+enable_testing()
+
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99")
+
+if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
+ set (CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/install" CACHE PATH "Default install path" FORCE )
+endif()
+
+#set(CMAKE_BUILD_TYPE RELEASE)
+
+include_directories(src)
+
+set(SOURCE_FILES src/segyio/segy.c src/spec/segyspec.c)
+
+install(FILES src/segyio/segy.h DESTINATION ${CMAKE_INSTALL_PREFIX}/include/segyio)
+
+add_library(segyio-static STATIC ${SOURCE_FILES})
+set_target_properties(segyio-static PROPERTIES OUTPUT_NAME segyio CLEAN_DIRECT_OUTPUT 1)
+set_target_properties(segyio-static PROPERTIES COMPILE_FLAGS "-fPIC")
+
+add_library(segyio-shared SHARED ${SOURCE_FILES})
+set_target_properties(segyio-shared PROPERTIES OUTPUT_NAME segyio CLEAN_DIRECT_OUTPUT 1)
+
+install(TARGETS segyio-static segyio-shared DESTINATION ${CMAKE_INSTALL_PREFIX}/lib)
+
+add_executable(segyinfo src/applications/segyinfo.c)
+target_link_libraries(segyinfo segyio-static)
+add_dependencies(segyinfo segyio-static)
+
+add_executable(segyinspect src/applications/segyinspect.c)
+target_link_libraries(segyinspect segyio-static)
+add_dependencies(segyinspect segyio-static)
+
+install(TARGETS segyinfo DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
+install(TARGETS segyinspect DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
+
+if (BUILD_MEX)
+ add_subdirectory(mex)
+else (BUILD_MEX)
+ unset(MATLAB_MCC CACHE)
+ unset(MATLAB_MEX CACHE)
+ unset(MATLAB_MEXEXT CACHE)
+ unset(MATLAB_ROOT CACHE)
+endif()
+
+add_subdirectory(python)
+add_subdirectory(tests)
+add_subdirectory(examples)
diff --git a/License.md b/License.md
new file mode 100644
index 0000000..4900f40
--- /dev/null
+++ b/License.md
@@ -0,0 +1,157 @@
+### GNU LESSER GENERAL PUBLIC LICENSE
+
+Version 3, 29 June 2007
+
+Copyright (C) 2007 Free Software Foundation, Inc.
+<http://fsf.org/>
+
+Everyone is permitted to copy and distribute verbatim copies of this
+license document, but changing it is not allowed.
+
+This version of the GNU Lesser General Public License incorporates the
+terms and conditions of version 3 of the GNU General Public License,
+supplemented by the additional permissions listed below.
+
+#### 0. Additional Definitions.
+
+As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the
+GNU General Public License.
+
+"The Library" refers to a covered work governed by this License, other
+than an Application or a Combined Work as defined below.
+
+An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+#### 1. Exception to Section 3 of the GNU GPL.
+
+You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+#### 2. Conveying Modified Versions.
+
+If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+- a) under this License, provided that you make a good faith effort
+ to ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+- b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+#### 3. Object Code Incorporating Material from Library Header Files.
+
+The object code form of an Application may incorporate material from a
+header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+- a) Give prominent notice with each copy of the object code that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+- b) Accompany the object code with a copy of the GNU GPL and this
+ license document.
+
+#### 4. Combined Works.
+
+You may convey a Combined Work under terms of your choice that, taken
+together, effectively do not restrict modification of the portions of
+the Library contained in the Combined Work and reverse engineering for
+debugging such modifications, if you also do each of the following:
+
+- a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+- b) Accompany the Combined Work with a copy of the GNU GPL and this
+ license document.
+- c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+- d) Do one of the following:
+ - 0) Convey the Minimal Corresponding Source under the terms of
+ this License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+ - 1) Use a suitable shared library mechanism for linking with
+ the Library. A suitable mechanism is one that (a) uses at run
+ time a copy of the Library already present on the user's
+ computer system, and (b) will operate properly with a modified
+ version of the Library that is interface-compatible with the
+ Linked Version.
+- e) Provide Installation Information, but only if you would
+ otherwise be required to provide such information under section 6
+ of the GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the Application
+ with a modified version of the Linked Version. (If you use option
+ 4d0, the Installation Information must accompany the Minimal
+ Corresponding Source and Corresponding Application Code. If you
+ use option 4d1, you must provide the Installation Information in
+ the manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.)
+
+#### 5. Combined Libraries.
+
+You may place library facilities that are a work based on the Library
+side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+- a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities, conveyed under the terms of this License.
+- b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+#### 6. Revised Versions of the GNU Lesser General Public License.
+
+The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+as you received it specifies that a certain numbered version of the
+GNU Lesser General Public License "or any later version" applies to
+it, you have the option of following the terms and conditions either
+of that published version or of any later version published by the
+Free Software Foundation. If the Library as you received it does not
+specify a version number of the GNU Lesser General Public License, you
+may choose any version of the GNU Lesser General Public License ever
+published by the Free Software Foundation.
+
+If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..6498f12
--- /dev/null
+++ b/README.md
@@ -0,0 +1,113 @@
+# SEGY IO #
+
+## Introduction ##
+
+Segyio is a small LGPL licensed C library for easy interaction with SEG Y
+formatted seismic data, with language bindings for Python and Matlab. Segyio is
+an attempt to create an easy-to-use, embeddable, community-oriented library for
+seismic applications. Features are added as they are needed; suggestions and
+contributions of all kinds are very welcome.
+
+## Feature summary ##
+ * A low-level C interface with few assumptions; easy to bind to other
+ languages.
+ * Read and write binary and textual headers.
+ * Read and write traces, trace headers.
+ * Easy to use and native-feeling python interface with numpy integration.
+
+## Getting started ##
+
+When Segyio is built and installed, you're ready to start programming! For
+examples and documentation, check out the examples in the examples directory.
+If you're using python, pydoc is used, so fire up your favourite python
+interpreter and type `help(segyio)` to get started.
+
+### Requirements ###
+
+To build and use Segyio you need:
+ * A C99 compatible C compiler (tested mostly on gcc and clang)
+ * [CMake](https://cmake.org/) version 2.8 or greater
+ * [Python](https://www.python.org/) 2.7. We're working on 3.x support, help
+ appreciated!
+
+### Building ###
+
+#### Users ####
+
+To build and install Segyio, perform the following actions in your console:
+
+```
+git clone https://github.com/Statoil/segyio
+cd segyio
+mkdir build
+cmake .. -DCMAKE_BUILD_TYPE=Release
+make
+make install
+```
+
+Make install must be done as root for a system install; if you want to install
+in your home directory, add `-DCMAKE_INSTALL_PREFIX=~/` or some other
+approperiate directory. Remember to update your $PATH!
+
+##### Matlab support #####
+
+To build the matlab bindings, invoke CMake with the option `-DBUILD_MEX=ON`. In
+some environments the Matlab binaries are in a non-standard location, in which
+case you need to help CMake find the matlab binaries by passing
+`-DMATLAB_ROOT=/path/to/matlab`.
+
+#### Developers ####
+
+It's recommended to build in debug mode to get more warnings and to embed debug
+symbols in the objects. Substituting `Debug` for `Release` in the
+`CMAKE_BUILD_TYPE` is plenty.
+
+Tests are located in the tests directory, and it's highly recommended that new
+features added are demonstrated for correctness and contract by adding a test.
+Feel free to use the tests already written as a guide.
+
+After building Segyio you can run the tests with `ctest`, executed from the
+build directory.
+
+Please note that to run the python tests you need to let your environment know
+where to find the segyio library files. On linux (bash) this is accomplished by being
+in the build directory and executing: `LD_LIBRARY_PATH=$PWD:$LD_LIBRARY_PATH`
+or by passing ctest a modified environment
+`LD_LIBRARY_PATH=$PWD:$LD_LIBRARY_PATH ctest`.
+
+## Contributing ##
+
+We welcome all kinds of contributions, including code, bug reports, issues,
+feature requests and documentation. The preferred way of submitting a
+contribution is to either make an
+[issue](https://github.com/Statoil/SegyIO/issues) on github or by forking the
+project on github and making a pull request.
+
+The current version supports:
+
+* Reading EBCDIC Textual Header
+* Reading Binary Header and selected fields.
+* Read individual traces
+* Inspect a SEGY file and identify crossline, inline and sample indexes
+
+
+## Reproducing the test data ##
+
+Small SEG Y formatted files are included in the repository for test purposes.
+Phyiscally speaking the data is non-sensical, but it is reproducible by using
+Segyio. The tests file are located in the tests/test-data directory. To
+reproduce the data file, build Segyio and run the test program `make-file.py`
+as such:
+
+```
+python examples/make-file.py out.sgy 50 1 6 20 25
+```
+
+If you have have small data files with a free license, feel free to submit it
+to the project!
+
+## History ##
+Segyio was initially written and is maintained by [Statoil
+ASA](http://www.statoil.com/) as a free, simple, easy-to-use way of interacting
+with seismic data that can be tailored to our needs, and as contribution to the
+free software community.
diff --git a/cmake/matlab.cmake b/cmake/matlab.cmake
new file mode 100644
index 0000000..d4e3a93
--- /dev/null
+++ b/cmake/matlab.cmake
@@ -0,0 +1,257 @@
+# This module looks for mex, the MATLAB compiler.
+# The following variables are defined when the script completes:
+# MATLAB_MEX: location of mex compiler
+# MATLAB_ROOT: root of MATLAB installation
+# MATLABMEX_FOUND: 0 if not found, 1 if found
+
+SET(MATLABMEX_FOUND 0)
+SET(MATLABMCC_FOUND 0)
+
+IF(WIN32)
+ # Win32 is Untested
+ # Taken from the older FindMatlab.cmake script as well as
+ # the modifications by Ramon Casero and Tom Doel for Gerardus.
+
+ # Search for a version of Matlab available, starting from the most modern one
+ # to older versions.
+ FOREACH(MATVER "7.20" "7.19" "7.18" "7.17" "7.16" "7.15" "7.14" "7.13" "7.12" "7.11" "7.10" "7.9" "7.8" "7.7" "7.6" "7.5" "7.4")
+ IF((NOT DEFINED MATLAB_ROOT)
+ OR ("${MATLAB_ROOT}" STREQUAL "")
+ OR ("${MATLAB_ROOT}" STREQUAL "/registry"))
+ GET_FILENAME_COMPONENT(MATLAB_ROOT
+ "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MathWorks\\MATLAB\\${MATVER};MATLABROOT]"
+ ABSOLUTE)
+ SET(MATLAB_VERSION ${MATVER})
+ ENDIF((NOT DEFINED MATLAB_ROOT)
+ OR ("${MATLAB_ROOT}" STREQUAL "")
+ OR ("${MATLAB_ROOT}" STREQUAL "/registry"))
+ ENDFOREACH(MATVER)
+
+ FIND_PROGRAM(MATLAB_MEX
+ mex
+ ${MATLAB_ROOT}/bin
+ )
+ FIND_PROGRAM(MATLAB_MCC
+ mex
+ ${MATLAB_ROOT}/bin
+ )
+ELSE(WIN32)
+ # Check if this is a Mac.
+ IF(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
+ # Mac is untested
+ # Taken from the older FindMatlab.cmake script as
+ # well as the modifications by Ramon Casero and Tom Doel for Gerardus.
+
+ SET(LIBRARY_EXTENSION .dylib)
+
+ # If this is a Mac and the attempts to find MATLAB_ROOT have so far failed,~
+ # we look in the applications folder
+ IF((NOT DEFINED MATLAB_ROOT) OR ("${MATLAB_ROOT}" STREQUAL ""))
+
+ # Search for a version of Matlab available, starting from the most modern
+ # one to older versions
+ FOREACH(MATVER "R2013b" "R2013a" "R2012b" "R2012a" "R2011b" "R2011a" "R2010b" "R2010a" "R2009b" "R2009a" "R2008b")
+ IF((NOT DEFINED MATLAB_ROOT) OR ("${MATLAB_ROOT}" STREQUAL ""))
+ IF(EXISTS /Applications/MATLAB_${MATVER}.app)
+ SET(MATLAB_ROOT /Applications/MATLAB_${MATVER}.app)
+
+ ENDIF(EXISTS /Applications/MATLAB_${MATVER}.app)
+ ENDIF((NOT DEFINED MATLAB_ROOT) OR ("${MATLAB_ROOT}" STREQUAL ""))
+ ENDFOREACH(MATVER)
+
+ ENDIF((NOT DEFINED MATLAB_ROOT) OR ("${MATLAB_ROOT}" STREQUAL ""))
+
+ FIND_PROGRAM(MATLAB_MEX
+ mex
+ PATHS
+ ${MATLAB_ROOT}/bin
+ )
+ FIND_PROGRAM(MATLAB_MCC
+ mcc
+ PATHS
+ ${MATLAB_ROOT}/bin
+ )
+
+ ELSE(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
+ # On a Linux system. The goal is to find MATLAB_ROOT.
+ SET(LIBRARY_EXTENSION .so)
+
+ FIND_PROGRAM(MATLAB_MEX
+ mex
+ PATHS
+ /prog/matlab/R2014B/bin # Statoil location
+ ${MATLAB_ROOT}/bin
+ /opt/matlab/bin
+ /usr/local/matlab/bin
+ $ENV{HOME}/matlab/bin
+ # Now all the versions
+ /opt/matlab/[rR]20[0-9][0-9][abAB]/bin
+ /usr/local/matlab/[rR]20[0-9][0-9][abAB]/bin
+ /opt/matlab-[rR]20[0-9][0-9][abAB]/bin
+ /opt/matlab_[rR]20[0-9][0-9][abAB]/bin
+ /usr/local/matlab-[rR]20[0-9][0-9][abAB]/bin
+ /usr/local/matlab_[rR]20[0-9][0-9][abAB]/bin
+ $ENV{HOME}/matlab/[rR]20[0-9][0-9][abAB]/bin
+ $ENV{HOME}/matlab-[rR]20[0-9][0-9][abAB]/bin
+ $ENV{HOME}/matlab_[rR]20[0-9][0-9][abAB]/bin
+ )
+
+ GET_FILENAME_COMPONENT(MATLAB_MEX "${MATLAB_MEX}" REALPATH)
+ GET_FILENAME_COMPONENT(MATLAB_BIN_ROOT "${MATLAB_MEX}" PATH)
+ # Strip ./bin/.
+ GET_FILENAME_COMPONENT(MATLAB_ROOT "${MATLAB_BIN_ROOT}" PATH)
+
+ FIND_PROGRAM(MATLAB_MCC
+ mcc
+ PATHS
+ ${MATLAB_ROOT}/bin
+ )
+
+ FIND_PROGRAM(MATLAB_MEXEXT
+ mexext
+ PATHS
+ ${MATLAB_ROOT}/bin
+ )
+
+ GET_FILENAME_COMPONENT(MATLAB_MCC "${MATLAB_MCC}" REALPATH)
+ GET_FILENAME_COMPONENT(MATLAB_MEXEXT "${MATLAB_MEXEXT}" REALPATH)
+ ENDIF(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
+ENDIF(WIN32)
+
+IF(NOT EXISTS "${MATLAB_MEX}" AND "${MatlabMex_FIND_REQUIRED}")
+ MESSAGE(FATAL_ERROR "Could not find MATLAB mex compiler; try specifying MATLAB_ROOT.")
+ELSE(NOT EXISTS "${MATLAB_MEX}" AND "${MatlabMex_FIND_REQUIRED}")
+ IF(EXISTS "${MATLAB_MEX}")
+ MESSAGE(STATUS "Found MATLAB mex compiler: ${MATLAB_MEX}")
+ MESSAGE(STATUS "MATLAB root: ${MATLAB_ROOT}")
+ SET(MATLABMEX_FOUND 1)
+ ENDIF(EXISTS "${MATLAB_MEX}")
+ENDIF(NOT EXISTS "${MATLAB_MEX}" AND "${MatlabMex_FIND_REQUIRED}")
+
+IF(NOT EXISTS "${MATLAB_MCC}" AND "${MatlabMcc_FIND_REQUIRED}")
+ MESSAGE(FATAL_ERROR "Could not find MATLAB mcc compiler; try specifying MATLAB_ROOT.")
+ELSE(NOT EXISTS "${MATLAB_MCC}" AND "${MatlabMcc_FIND_REQUIRED}")
+ IF(EXISTS "${MATLAB_MCC}")
+ MESSAGE(STATUS "Found MATLAB mcc compiler: ${MATLAB_MCC}")
+ SET(MATLABMCC_FOUND 1)
+ ENDIF(EXISTS "${MATLAB_MCC}")
+ENDIF(NOT EXISTS "${MATLAB_MCC}" AND "${MatlabMcc_FIND_REQUIRED}")
+
+MARK_AS_ADVANCED(
+ MATLABMEX_FOUND
+)
+
+SET(MATLAB_ROOT ${MATLAB_ROOT} CACHE FILEPATH "Path to a matlab installation")
+
+EXECUTE_PROCESS(COMMAND ${MATLAB_MEXEXT} OUTPUT_VARIABLE MEXEXT OUTPUT_STRIP_TRAILING_WHITESPACE)
+
+macro(mexo MEX_OBJECT)
+ set(MEX_CFLAGS -fPIC -std=c99 -Werror)
+ set(MEX_LDFLAGS)
+
+ get_property(dirs DIRECTORY . PROPERTY INCLUDE_DIRECTORIES)
+ foreach(dir ${dirs})
+ set(MEX_CFLAGS ${MEX_CFLAGS} -I${dir})
+ endforeach()
+
+ set(MEX_LDFLAGS -shared)
+
+ set(SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/${MEX_OBJECT}.c)
+ set(HEADER ${CMAKE_CURRENT_SOURCE_DIR}/${MEX_OBJECT}.h)
+ set(OBJECT ${CMAKE_CURRENT_BINARY_DIR}/${MEX_OBJECT}.o)
+
+ add_custom_command(OUTPUT ${OBJECT}
+ COMMAND
+ ${MATLAB_MEX}
+ -c
+ CC="${CMAKE_C_COMPILER}"
+ LD="${CMAKE_CXX_COMPILER}"
+ CFLAGS="${MEX_CFLAGS}"
+ LDFLAGS="${MEX_LDFLAGS}"
+ -outdir ${CMAKE_CURRENT_BINARY_DIR}
+ ${SOURCE}
+ DEPENDS
+ ${SOURCE}
+ ${HEADER}
+ )
+
+ add_custom_target(${MEX_OBJECT} ALL DEPENDS ${OBJECT})
+endmacro()
+
+macro(mex MEX_NAME )
+ set(DEP ${ARG2})
+ set(MEX_CFLAGS -fPIC -std=c99 -Werror)
+ set(MEX_LDFLAGS)
+
+ get_property(dirs DIRECTORY . PROPERTY INCLUDE_DIRECTORIES)
+ foreach(dir ${dirs})
+ set(MEX_CFLAGS ${MEX_CFLAGS} -I${dir})
+ endforeach()
+
+ set(MEX_LDFLAGS -shared)
+
+ set(MEX_SOURCE_FILE ${CMAKE_CURRENT_SOURCE_DIR}/${MEX_NAME}.c)
+ set(MEX_RESULT_FILE ${CMAKE_CURRENT_BINARY_DIR}/${MEX_NAME}.${MEXEXT})
+ add_custom_command(OUTPUT ${MEX_RESULT_FILE}
+ COMMAND
+ ${MATLAB_MEX}
+ CC="${CMAKE_C_COMPILER}"
+ CXX="${CMAKE_CXX_COMPILER}"
+ LD="${CMAKE_CXX_COMPILER}"
+ CFLAGS="${MEX_CFLAGS}"
+ LDFLAGS="${MEX_LDFLAGS}"
+ ${OBJECT}
+ $<TARGET_FILE:segyio-static>
+ -outdir ${CMAKE_CURRENT_BINARY_DIR}
+ ${MEX_SOURCE_FILE}
+ DEPENDS
+ ${MEX_SOURCE_FILE}
+ segyio-static
+ segyutil.o
+ )
+
+ add_custom_target(${MEX_NAME} ALL DEPENDS ${MEX_RESULT_FILE} segyutil.o)
+
+ set(${MEX_NAME}_TARGET ${MEX_NAME} PARENT_SCOPE)
+ set(${MEX_NAME}_FILE ${MEX_RESULT_FILE} PARENT_SCOPE)
+
+endmacro()
+
+
+# this isn't meant to be run directly; use matlab_add_test or
+# matlab_add_example instead
+function(matlab_test TYPE TESTNAME MCC_SOURCE_FILE MCC_TARGET_NAME)
+ set(RESULT_FILE ${CMAKE_CURRENT_BINARY_DIR}/${MCC_TARGET_NAME})
+
+ add_custom_command(OUTPUT ${RESULT_FILE}
+ COMMAND
+ ${MATLAB_MCC}
+ -I ${CMAKE_BINARY_DIR}/mex
+ -m ${MCC_TARGET_NAME}
+ -d ${CMAKE_CURRENT_BINARY_DIR}
+ DEPENDS
+ ${CMAKE_CURRENT_LIST_DIR}/${MCC_SOURCE_FILE}
+ ${segyspec_mex_FILE}
+ ${segyheaders_mex_FILE}
+ ${CMAKE_SOURCE_DIR}/mex/SegySpec.m
+ WORKING_DIRECTORY
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ )
+
+ add_custom_target(${MCC_TARGET_NAME} ALL DEPENDS ${RESULT_FILE} ${segyspec_mex_TARGET} ${segyheaders_mex_TARGET})
+
+ add_test(NAME ${TESTNAME}
+ WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/${TYPE}
+ COMMAND run_${MCC_TARGET_NAME}.sh ${MATLAB_ROOT} ${ARGN}
+ )
+endfunction()
+
+function(add_matlab_test TESTNAME MCC_SOURCE_FILE MCC_TARGET_NAME)
+ matlab_test(tests ${TESTNAME} ${MCC_SOURCE_FILE} ${MCC_TARGET_NAME})
+endfunction()
+
+# add_matlab_example takes an arbitrary number of arguments which it will
+# forward to the example program
+function(add_matlab_example TESTNAME MCC_SOURCE_FILE MCC_TARGET_NAME)
+ matlab_test(examples ${TESTNAME} ${MCC_SOURCE_FILE} ${MCC_TARGET_NAME} ${ARGN})
+endfunction()
diff --git a/cmake/python.cmake b/cmake/python.cmake
new file mode 100644
index 0000000..56fead3
--- /dev/null
+++ b/cmake/python.cmake
@@ -0,0 +1,57 @@
+configure_file(cmake/test_runner.py tests/test_runner.py COPYONLY)
+
+if("${CMAKE_HOST_SYSTEM}" MATCHES ".*Windows.*")
+ set(SEP "\\;")
+else() # e.g. Linux
+ set(SEP ":")
+endif()
+
+function(add_memcheck_test NAME BINARY)
+ set(memcheck_command "valgrind --trace-children=yes --leak-check=full --error-exitcode=31415")
+ separate_arguments(memcheck_command)
+ add_test(memcheck_${NAME} ${memcheck_command} ./${BINARY})
+endfunction(add_memcheck_test)
+
+function(add_python_package PACKAGE_NAME PACKAGE_PATH PYTHON_FILES)
+ add_custom_target(package_${PACKAGE_NAME} ALL)
+
+ foreach (file ${PYTHON_FILES})
+ add_custom_command(TARGET package_${PACKAGE_NAME}
+ COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/python/${PACKAGE_PATH}
+ COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/${file} ${CMAKE_BINARY_DIR}/python/${PACKAGE_PATH}
+ )
+ endforeach ()
+ install(FILES ${PYTHON_FILES} DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/python2.7/site-packages/${PACKAGE_PATH})
+endfunction()
+
+function(add_python_test TESTNAME PYTHON_TEST_FILE)
+ configure_file(${PYTHON_TEST_FILE} ${PYTHON_TEST_FILE} COPYONLY)
+
+ add_test(NAME ${TESTNAME}
+ WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/tests
+ COMMAND python test_runner.py ${PYTHON_TEST_FILE}
+ )
+ set_tests_properties(${TESTNAME} PROPERTIES ENVIRONMENT
+ "PYTHONPATH=${CMAKE_BINARY_DIR}/python${SEP}$ENV{PYTHONPATH}"
+ )
+endfunction()
+
+function(add_python_example TESTNAME PYTHON_TEST_FILE)
+ configure_file(${PYTHON_TEST_FILE} ${PYTHON_TEST_FILE} COPYONLY)
+
+ add_test(NAME ${TESTNAME}
+ WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/examples
+ COMMAND python ${PYTHON_TEST_FILE} ${ARGN}
+ )
+ set_tests_properties(${TESTNAME} PROPERTIES ENVIRONMENT
+ "PYTHONPATH=${CMAKE_BINARY_DIR}/python${SEP}$ENV{PYTHONPATH}"
+ )
+endfunction()
+
+function(add_segyio_test TESTNAME TEST_SOURCES)
+ add_executable(test_${TESTNAME} unittest.h "${TEST_SOURCES}")
+ target_link_libraries(test_${TESTNAME} segyio-static m)
+ add_dependencies(test_${TESTNAME} segyio-static)
+ add_test(NAME ${TESTNAME} COMMAND ${EXECUTABLE_OUTPUT_PATH}/test_${TESTNAME})
+ add_memcheck_test(${TESTNAME} ${EXECUTABLE_OUTPUT_PATH}/test_${TESTNAME})
+endfunction()
diff --git a/cmake/test_runner.py b/cmake/test_runner.py
new file mode 100755
index 0000000..31e6fb9
--- /dev/null
+++ b/cmake/test_runner.py
@@ -0,0 +1,48 @@
+#!/usr/bin/env python
+import inspect
+import os
+import sys
+
+import imp
+
+try:
+ from unittest2 import TextTestRunner, TestLoader, TestCase
+except ImportError:
+ from unittest import TextTestRunner, TestLoader, TestCase
+
+
+def runTestCase(tests, verbosity=0):
+ test_result = TextTestRunner(verbosity=verbosity).run(tests)
+
+ if len(test_result.errors) or len(test_result.failures):
+ test_result.printErrors()
+ return False
+ else:
+ return True
+
+def getTestClassFromModule(module_path):
+ test_module = imp.load_source('test', module_path)
+ for name, obj in inspect.getmembers(test_module):
+ if inspect.isclass(obj) and issubclass(obj, TestCase) and not obj == TestCase:
+ return obj
+
+def getTestsFromModule(module_path):
+ klass = getTestClassFromModule(module_path)
+ if klass is None:
+ raise UserWarning("No tests classes found in: '%s'" % module_path)
+
+ loader = TestLoader()
+ return loader.loadTestsFromTestCase(klass)
+
+
+if __name__ == '__main__':
+ test_module = sys.argv[1]
+ argv = []
+
+ tests = getTestsFromModule(test_module)
+
+ # Set verbosity to 2 to see which test method in a class that fails.
+ if runTestCase(tests, verbosity=0):
+ sys.exit(0)
+ else:
+ sys.exit(1)
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
new file mode 100644
index 0000000..46036bb
--- /dev/null
+++ b/examples/CMakeLists.txt
@@ -0,0 +1,6 @@
+configure_file(../tests/test-data/small.sgy test-data/small.sgy COPYONLY)
+
+add_python_example(python.examples.about about.py test-data/small.sgy INLINE_3D CROSSLINE_3D)
+add_python_example(python.examples.write write.py test-data/small.sgy)
+add_python_example(python.examples.makefile make-file.py test-data/large-file.sgy 20 1 20 1 20)
+add_python_example(python.examples.subcube copy-sub-cube.py test-data/small.sgy test-data/copy.sgy)
diff --git a/examples/about.py b/examples/about.py
new file mode 100644
index 0000000..91a7649
--- /dev/null
+++ b/examples/about.py
@@ -0,0 +1,49 @@
+import sys
+from segyio import TraceField
+import segyio
+
+def list_byte_offset_names():
+ print("Available offsets and their corresponding byte value:")
+ for x in TraceField.enums():
+ print(" %s: %d" % (str(x), x))
+
+if __name__ == '__main__':
+ if len( sys.argv ) < 4:
+ list_byte_offset_names()
+ sys.exit( "Usage: about.py [file] [inline] [crossline]" )
+
+ # we need a way to convert from run-time inline/crossline argument (as
+ # text) to the internally used TraceField enum. Make a string -> TraceField
+ # map and look up into that. this dictionary comprehension creates that
+ fieldmap = { str( x ).lower(): x for x in TraceField.enums() }
+
+ filename = sys.argv[ 1 ]
+ inline_name, crossline_name = sys.argv[ 2 ].lower(), sys.argv[ 3 ].lower()
+
+ # exit if inline or crossline are unknown
+ if inline_name not in fieldmap:
+ list_byte_offset_names()
+ sys.exit( "Unknown inline field '%s'" % sys.argv[ 2 ] )
+
+ if crossline_name not in fieldmap:
+ list_byte_offset_names()
+ sys.exit( "Unknown crossline field '%s'" % sys.argv[ 3 ] )
+
+ inline, crossline = fieldmap[ inline_name ], fieldmap[ crossline_name ]
+
+ with segyio.open(filename, "r", inline, crossline) as f:
+ print("About '%s':" % filename)
+ print("Format type: %s" % f.format)
+ print("Offset count: %d" % f.offsets)
+ print("ilines: %s" % ", ".join(map(str, f.ilines)))
+ print("xlines: %s" % ", ".join(map(str, f.xlines)))
+
+ print "+------+"
+
+ with segyio.open(filename, "r", crossline, inline) as f:
+ # with swapped inline/crossline
+ print("About '%s':" % filename)
+ print("Format type: %s" % f.format)
+ print("Offset count: %d" % f.offsets)
+ print("ilines: %s" % ", ".join(map(str, f.ilines)))
+ print("xlines: %s" % ", ".join(map(str, f.xlines)))
diff --git a/examples/copy-sub-cube.py b/examples/copy-sub-cube.py
new file mode 100644
index 0000000..900680e
--- /dev/null
+++ b/examples/copy-sub-cube.py
@@ -0,0 +1,41 @@
+import sys
+import segyio
+
+# this program creates a new subcube, taking the first 5 lines in both
+# directions, and reduces the trace size to 20 samples
+def main():
+ if len(sys.argv) < 3:
+ sys.exit("Usage: {} [source-file] [destination-file]".format(sys.argv[0]))
+
+ sourcefile = sys.argv[1]
+ destfile = sys.argv[2]
+
+ with segyio.open(sourcefile) as src:
+ spec = segyio.spec()
+ spec.sorting = int(src.sorting)
+ spec.format = int(src.format)
+ spec.samples = 50
+ spec.ilines = src.ilines[:5]
+ spec.xlines = src.xlines[:5]
+
+ with segyio.create(destfile, spec) as dst:
+ # Copy all textual headers, including possible extended
+ for i in range(1 + src.ext_headers):
+ dst.text[i] = src.text[i]
+
+ # copy the binary header, then insert the modifications needed for
+ # the new shape
+ dst.bin = src.bin
+ dst.bin = { segyio.BinField.Samples: 50,
+ segyio.BinField.Traces: 5 * 5
+ }
+
+ # Copy all headers in the new inlines. Since we know we're copying
+ # the five first we don't have to take special care to update
+ # headers
+ dst.header.iline = src.header.iline
+ # the copy traces (in line mode)
+ dst.iline = src.iline
+
+if __name__ == '__main__':
+ main()
diff --git a/examples/make-file.py b/examples/make-file.py
new file mode 100644
index 0000000..8179b18
--- /dev/null
+++ b/examples/make-file.py
@@ -0,0 +1,55 @@
+import sys
+import numpy as np
+import segyio
+from itertools import izip as izip
+
+def main():
+ if len(sys.argv) < 7:
+ sys.exit("Usage: {} [file] [samples] [first iline] [last iline] [first xline] [last xline]".format(sys.argv[0]))
+
+ spec = segyio.spec()
+ filename = sys.argv[1]
+
+# to create a file from nothing, we need to tell segyio about the structure of
+# the file, i.e. its inline numbers, crossline numbers, etc. You can also add
+# more structural information, but offsets etc. have sensible defautls. This is
+# the absolute minimal specification for a N-by-M volume
+ spec.sorting = 2
+ spec.format = 1
+ spec.samples = int(sys.argv[2])
+ spec.ilines = range(*map(int, sys.argv[3:5]))
+ spec.xlines = range(*map(int, sys.argv[5:7]))
+
+ with segyio.create(filename, spec) as f:
+ # one inline consists of 50 traces
+ # which in turn consists of 2000 samples
+ start = 0.0
+ step = 0.00001
+ # fill a trace with predictable values: left-of-comma is the inline
+ # number. Immediately right of comma is the crossline number
+ # the rightmost digits is the index of the sample in that trace meaning
+ # looking up an inline's i's jth crosslines' k should be roughly equal
+ # to i.j0k
+ trace = np.arange(start = start,
+ stop = start + step * spec.samples,
+ step = step,
+ dtype = np.float32)
+
+ # one inline is N traces concatenated. We fill in the xline number
+ line = np.concatenate([trace + (xl / 100.0) for xl in spec.xlines])
+
+ # write the line itself to the file
+ # write the inline number in all this line's headers
+ for ilno in spec.ilines:
+ f.iline[ilno] = (line + ilno)
+ f.header.iline[ilno] = { segyio.TraceField.INLINE_3D: ilno,
+ segyio.TraceField.offset: 1
+ }
+
+ # then do the same for xlines
+ for xlno in spec.xlines:
+ f.header.xline[xlno] = { segyio.TraceField.CROSSLINE_3D: xlno }
+
+
+if __name__ == '__main__':
+ main()
diff --git a/examples/segyview.py b/examples/segyview.py
new file mode 100644
index 0000000..b3b6c01
--- /dev/null
+++ b/examples/segyview.py
@@ -0,0 +1,32 @@
+import sys
+import segyio
+import numpy as np
+import matplotlib.pyplot as plt
+from pylab import *
+import time
+
+def main():
+ if len( sys.argv ) < 2:
+ sys.exit("Usage: segyview.py [file]")
+
+
+ filename = sys.argv[1]
+ data = None
+ ion()
+
+ with segyio.open( filename, "r" ) as f:
+ data = f.xline[1428]
+ #plt.colorbar()
+ imgplot = plt.imshow(data, vmin=1500, vmax=1600)
+ plt.show(False)
+ for i in range(1429,1440):
+ data = f.xline[i]
+ imgplot.set_data(data)
+ plt.draw()
+ plt.pause(0.1) #Note this correction
+
+ plt.show()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/examples/write.py b/examples/write.py
new file mode 100644
index 0000000..68ebb47
--- /dev/null
+++ b/examples/write.py
@@ -0,0 +1,84 @@
+import sys
+import segyio
+import numpy as np
+
+def main():
+ if len( sys.argv ) < 2:
+ sys.exit("Usage: write.py [file]")
+
+ filename = sys.argv[1]
+
+ # the mode parameter is passed directly to C's fopen
+ # opening the file for writing requires r+, not rw because rw would
+ # truncate (effectively destroy) the file, and r+ would preserve the size
+ with segyio.open( filename, "r+" ) as src:
+
+ # read trace 0, then double all values
+ trace = src.trace[0]
+ trace *= 2
+
+ # write trace 0 back to disk
+ src.trace[0] = trace
+
+ # read trace 1, but re-use the memory for speed
+ trace = src.trace[1, trace]
+ # square all values. the trace is a plain numpy array
+ trace = np.square(trace, trace)
+ # write the trace back to disk, but at trace 2
+ src.trace[2] = trace
+
+ # read every other trace, from 10 through 20
+ # then write them to every third step from 40 through 52
+ # i.e. 40, 43, 46...
+ # slices yield a generator, so only one numpy array is created
+ for tr, i in zip(src.trace[10:20:2], range(2,13,3)):
+ src.trace[i] = tr
+
+ # iterate over all traces in a file. this is a generator with a shared
+ # buffer, so it's quite efficient
+ tracesum = 0
+ for tr in src.trace:
+ # accumulate the traces' 30th value
+ tracesum += tr[30]
+
+ print("Trace sum: %f" % tracesum)
+
+ # write the iline at 2 to the iline at 3
+ sum3 = np.sum(src.iline[3])
+ src.iline[2] = src.iline[3]
+ # flush to make sure our changes to the file are visible
+ src.flush()
+ sum2 = np.sum(src.iline[2])
+
+ print("Accumulates of inlines 2 and 3: %f -- %f" % (sum2, sum3))
+
+ # ilines too are plain numpy ndarrays, with trace-major addressing
+ # i.e. iline[2,40] would be yield trace#2's 40th value
+ iline = src.iline[2]
+ # since ilines are numpy arrays they also support numpy operations
+ iline = np.add(iline, src.iline[4])
+
+ # lines too have generator support, so we accumulate the 2nd trace's
+ # 22nd value.
+ linesum = 0
+ for line in src.iline:
+ linesum += line[2,22]
+
+ print("Inline sum: %f" % linesum)
+
+ # xline access is identical to iline access
+ linesum = 0
+ for line in src.xline:
+ linesum += line[2,22]
+
+ print("Crossline sum: %f" % linesum)
+
+ # accessing a non-existing inline will raise a KeyError
+ try:
+ src.iline[5000]
+ assert(False)
+ except KeyError as e:
+ print(str(e))
+
+if __name__ == '__main__':
+ main()
diff --git a/mex/CMakeLists.txt b/mex/CMakeLists.txt
new file mode 100644
index 0000000..6fecdff
--- /dev/null
+++ b/mex/CMakeLists.txt
@@ -0,0 +1,40 @@
+include(../cmake/matlab.cmake REQUIRED)
+
+configure_file(Segy.m Segy.m)
+configure_file(SegySpec.m SegySpec.m)
+configure_file(SegySampleFormat.m SegySampleFormat.m)
+configure_file(TraceSortingFormat.m TraceSortingFormat.m)
+configure_file(TraceField.m TraceField.m)
+
+
+mexo(segyutil)
+mex(segyspec_mex)
+mex(segy_read_write_line_mex segyutil)
+mex(segy_get_header_mex segyutil)
+mex(segy_get_traces_mex segyutil)
+mex(segy_put_traces_mex segyutil)
+mex(segy_get_ntraces_mex segyutil)
+mex(segy_get_segy_header_mex segyutil)
+mex(segy_get_bfield_mex segyutil)
+mex(segy_get_trace_header_mex segyutil)
+mex(segy_get_field_mex segyutil)
+mex(segy_put_headers_mex segyutil)
+
+install(FILES
+ ${CMAKE_CURRENT_BINARY_DIR}/segyspec_mex.mexa64
+ ${CMAKE_CURRENT_BINARY_DIR}/segy_read_write_line_mex.mexa64
+ ${CMAKE_CURRENT_BINARY_DIR}/segy_get_header_mex.mexa64
+ ${CMAKE_CURRENT_BINARY_DIR}/segy_get_traces_mex.mexa64
+ ${CMAKE_CURRENT_BINARY_DIR}/segy_put_traces_mex.mexa64
+ ${CMAKE_CURRENT_BINARY_DIR}/segy_get_ntraces_mex.mexa64
+ ${CMAKE_CURRENT_BINARY_DIR}/segy_get_segy_header_mex.mexa64
+ ${CMAKE_CURRENT_BINARY_DIR}/segy_get_bfield_mex.mexa64
+ ${CMAKE_CURRENT_BINARY_DIR}/segy_get_trace_header_mex.mexa64
+ ${CMAKE_CURRENT_BINARY_DIR}/segy_get_field_mex.mexa64
+ ${CMAKE_CURRENT_BINARY_DIR}/segy_put_headers_mex.mexa64
+ SegySpec.m
+ Segy.m
+ SegySampleFormat.m
+ TraceSortingFormat.m
+ DESTINATION
+ ${CMAKE_INSTALL_PREFIX}/matlab)
diff --git a/mex/Segy.m b/mex/Segy.m
new file mode 100644
index 0000000..a23e871
--- /dev/null
+++ b/mex/Segy.m
@@ -0,0 +1,550 @@
+classdef Segy
+% add comment here
+ properties
+ spec
+ end
+
+ methods(Static)
+
+ function obj = readInLine(spec, index)
+ obj = segy_read_write_line_mex(spec, index, max(size(spec.crossline_indexes)), spec.inline_indexes, spec.il_stride);
+ end
+
+ function obj = readCrossLine(spec, index)
+ obj = segy_read_write_line_mex(spec, index, max(size(spec.inline_indexes)), spec.crossline_indexes, spec.xl_stride);
+ end
+
+ function obj = writeCrossLine(spec, data, index)
+ segy_read_write_line_mex(spec, index, max(size(spec.inline_indexes)), spec.crossline_indexes, spec.xl_stride, data);
+ obj = data;
+ end
+
+ function obj = writeInLine(spec, data, index)
+ segy_read_write_line_mex(spec, index, max(size(spec.crossline_indexes)), spec.inline_indexes, spec.il_stride, data);
+ obj = data;
+ end
+
+ function data = get_line(cube, dir, n)
+ if strcmpi(dir, 'iline')
+ data = segy_read_write_line_mex(cube, n, max(size(cube.crossline_indexes)), cube.inline_indexes, cube.il_stride);
+
+ elseif strcmpi(dir, 'xline')
+ data = segy_read_write_line_mex(cube, n, max(size(cube.inline_indexes)), cube.crossline_indexes, cube.xl_stride);
+
+ else
+ error('Only iline and xline are valid directions.');
+ end
+ end
+
+ function data = put_line(cube, data, dir, n)
+ if strcmpi(dir, 'iline')
+ segy_read_write_line_mex(cube, n, max(size(cube.crossline_indexes)), cube.inline_indexes, cube.il_stride, data);
+
+ elseif strcmpi(dir, 'xline')
+ segy_read_write_line_mex(cube, n, max(size(cube.inline_indexes)), cube.crossline_indexes, cube.xl_stride, data );
+
+ else
+ error('Only iline and xline are valid directions.');
+ end
+ end
+
+ % Goal:
+ % Fast reading of trace header words in segy filenamele.
+ %
+ % Algorithm:
+ % Use keyword as specified in available_headers. If byte location is
+ % known byte2headerword converts to keyword.
+ %
+ % Inputs:
+ % filename Filename of segyfile
+ % headword Name of header word to read (example: 'cdp')
+ % notype Optional. If number format is different than set in segy
+ % header this can be set by notype. Valid numbers are 1,2,3,5
+ % and 8 as spesified by SEG-Y rev 1 standard.
+ %
+ % Output:
+ % name meaning
+ % tr_heads Header values
+ function [tr_heads, notrace] = get_header(filename, headword, notype)
+ if exist(filename, 'file') ~= 2
+ error('File does not exist')
+ end
+ % notype isn't really use so we ignore it (for interface compatilibty)
+
+ if ischar(headword)
+ headword = TraceField.(headword);
+ end
+
+ headword = int32(headword);
+ [x, y] = segy_get_header_mex(filename, headword);
+ tr_heads = x;
+ notrace = y;
+ end
+
+ % [data,dt,notype] = get_traces(filename,n1,n2,notype)
+ %
+ % Goal:
+ % Fast reading of traces in segy volume. Not for front-end use. Use
+ % get_line / get_slice / get_subcube instead.
+ %
+ % Algorithm:
+ %
+ %
+ % Inputs:
+ % filename Filename of segyfile
+ % n1 (Optional) First trace. If no first and last trace is
+ % specified, all file is read.
+ % n2 (Optional) Last trace.
+ % notype Optional. If number format is different than set in segy
+ % header this can be set by notype. Valid numbers are 1,2,3,5
+ % and 8 as spesified by SEG-Y rev 1 standard.
+ %
+ % Output:
+ % data Traces read from file
+ % dt Sample interval
+ % notype Number format
+ function [data, dt, notype] = get_traces( filename, n1, n2, notype )
+ if exist(filename, 'file') ~= 2
+ error('File does not exist')
+ end
+
+ if nargin < 2
+ n1 = 1;
+ end
+ if nargin < 3
+ n2 = 0;
+ end
+
+ if nargin < 4
+ notype = -1;
+ end
+
+ [data, dt, notype] = segy_get_traces_mex( filename, n1 - 1, n2 - 1, notype );
+ end
+
+ %function ntraces = get_ntraces(filename);
+ %
+ % Goal
+ % Count the number of traces in a segy file
+ %
+ % Inputs:
+ % filename Filename of segyfile
+ %
+ % Output:
+ % notrace number of traces
+ % return 0 in case of error
+ function notrace = get_ntraces( filename )
+ if exist(filename, 'file') ~= 2
+ error('File does not exist')
+ end
+ notrace = segy_get_ntraces_mex( filename );
+ end
+
+ % Goal:
+ % Interpret segy cube as a 3D cube and save information needed to access
+ % the segy file as a cube in terms of inline and crossline numbers.
+ %
+ % inputs:
+ % filename filename of segy file
+ % il_word bytenumber or header word for inline number
+ % xl_word bytenumber or header word for crossline number
+ % t0 Time (ms) / depth (m) of first sample. Optional (default = 0)
+ %
+ % output:
+ % segycube struct needed to access segy file as a cube.
+
+ function segycube = interpret_segycube(filename, il_word, xl_word, t0)
+ if exist(filename, 'file') ~= 2
+ error('File does not exist')
+ end
+
+ if nargin < 4
+ t0 = 0;
+ end
+ if nargin < 3
+ xl_word = TraceField.CROSSLINE_3D;
+ end
+ if nargin < 2
+ il_word = TraceField.INLINE_3D;
+ end
+
+ % for compatibility with old code; if argument is passed as a
+ % string, first convert to an enum, then pass that enum to the
+ % constructor
+ if ischar(il_word)
+ il_word = TraceField.(il_word);
+ end
+
+ if ischar(xl_word)
+ xl_word = TraceField.(xl_word);
+ end
+
+ segycube = SegySpec(filename, il_word, xl_word, t0);
+ end
+
+ %
+ % Goal:
+ % Read a full cube from segycube struct.
+ %
+ % Inputs:
+ % sc Data as an interpreted segy cube from
+ % 'interpret_segycube.m'
+ % Output:
+ % data Extracted cube with same sorting as in segy.
+ %
+ function data = get_cube(sc)
+ data = Segy.get_traces(sc.filename);
+
+ if sc.trace_sorting_format == TraceSortingFormat.INLINE
+ data = reshape( data, size( sc.sample_indexes, 1 ), size( sc.xline, 1 ), size( sc.iline, 1 ) );
+ elseif sc.trace_sorting_format == TraceSortingFormat.XLINE
+ data = reshape( data, size( sc.sample_indexes, 1 ), size( sc.iline, 1 ), size( sc.xline, 1 ) );
+ else
+ warning('Sorting was not set properly. Data returned as single long line');
+ end
+ end
+
+ function [data, notype] = put_traces(filename, data, n1, n2, notype)
+ if exist(filename, 'file') ~= 2
+ error('File does not exist')
+ end
+
+ if nargin < 2
+ error('Too few arguments. Usage: put_traces( filename, data, (optional): n1, n2, notype)')
+ end
+
+ if nargin < 3
+ n1 = 1;
+ end
+
+ if nargin < 4
+ n2 = 0;
+ end
+
+ if nargin < 5
+ notype = -1;
+ end
+
+ % matlab uses 1-indexing, but C wants its positions 0-indexed.
+ [data, notype] = segy_put_traces_mex( filename, data, n1 - 1, n2 - 1, notype );
+ end
+
+
+ % function SegyHeader = get_segy_header(filename)
+ %
+ % Goal:
+ % Read segy header. Extended textual headers are not read
+ %
+ % Inputs:
+ % filename Filename of segyfile
+ %
+ % Output:
+ % SegyHeader Struct with entire segy header
+ function SegyHeader = get_segy_header(filename)
+ if exist(filename, 'file') ~= 2
+ error('File does not exist')
+ end
+
+ [ebcdic, bin] = segy_get_segy_header_mex( filename );
+
+ SegyHeader.ebcdic = ebcdic;
+ SegyHeader.JobIdNumber = segy_get_bfield_mex( bin, 3201 );
+ SegyHeader.LineNumber = segy_get_bfield_mex( bin, 3205 );
+ SegyHeader.ReelNumber = segy_get_bfield_mex( bin, 3209 );
+ SegyHeader.NumberOfTracesPerEnsemble = segy_get_bfield_mex( bin, 3213 );
+ SegyHeader.NumberOfAuxTracesPerEnsemble = segy_get_bfield_mex( bin, 3215 );
+ SegyHeader.SampleInterval = segy_get_bfield_mex( bin, 3217 );
+ SegyHeader.SampleIntervalOriginal = segy_get_bfield_mex( bin, 3219 );
+ SegyHeader.NumberOfSamples = segy_get_bfield_mex( bin, 3221 );
+ SegyHeader.NumberOfSamplesOriginal = segy_get_bfield_mex( bin, 3223 );
+
+ SegyHeader.SampleFormat = 0;
+ switch segy_get_bfield_mex( bin, 3225 )
+ case 1
+ SegyHeader.SampleFormat = 'IBM32';
+ case 2
+ SegyHeader.SampleFormat = 'INT32';
+ case 3
+ SegyHeader.SampleFormat = 'INT16';
+ case 4
+ SegyHeader.SampleFormat = 'Obsolete';
+ case 5
+ SegyHeader.SampleFormat = 'IEEE32';
+ case 6
+ SegyHeader.SampleFormat = 'NotUsed';
+ case 7
+ SegyHeader.SampleFormat = 'NotUsed';
+ case 8
+ SegyHeader.SampleFormat = 'INT8';
+ end
+
+ SegyHeader.EnsembleFold = segy_get_bfield_mex( bin, 3227 );
+
+ SegyHeader.TraceSortingCode = 0;
+ switch segy_get_bfield_mex( bin, 3229 )
+ case -1
+ SegyHeader.TraceSortingCode = 'Other';
+ case 0
+ SegyHeader.TraceSortingCode = 'Unknown';
+ case 1
+ SegyHeader.TraceSortingCode = 'AsRecorded';
+ case 2
+ SegyHeader.TraceSortingCode = 'CDP';
+ case 3
+ SegyHeader.TraceSortingCode = 'SingleFoldContinuousProfile';
+ case 4
+ SegyHeader.TraceSortingCode = 'HorizontallyStacked';
+ case 5
+ SegyHeader.TraceSortingCode = 'CommonSourcePoint';
+ case 6
+ SegyHeader.TraceSortingCode = 'CommonReceiverPoint';
+ case 7
+ SegyHeader.TraceSortingCode = 'CommonOffsetPoint';
+ case 8
+ SegyHeader.TraceSortingCode = 'CommonMidPoint';
+ case 9
+ SegyHeader.TraceSortingCode = 'CommonConversionPoint';
+ end
+
+ SegyHeader.VerticalSumCode = [num2str(segy_get_bfield_mex( bin, 3231 )), '_Sum'];
+ SegyHeader.SweepFrequencyAtStart = segy_get_bfield_mex( bin, 3233 );
+ SegyHeader.SweepFrequencyAtEnd = segy_get_bfield_mex( bin, 3235 );
+ SegyHeader.SweepLength = segy_get_bfield_mex( bin, 3237 );
+ SegyHeader.SweepTypeCode = 0;
+ switch segy_get_bfield_mex( bin, 3239 )
+ case 1
+ SegyHeader.SweepTypeCode = 'Linear';
+ case 2
+ SegyHeader.SweepTypeCode = 'Parabolic';
+ case 3
+ SegyHeader.SweepTypeCode = 'Exponential';
+ case 4
+ SegyHeader.SweepTypeCode = 'Other';
+ end
+
+ SegyHeader.TraceNoOfSweepChannel = segy_get_bfield_mex( bin, 3241 );
+ SegyHeader.SweepTraceTaperLenghtStart = segy_get_bfield_mex( bin, 3243 );
+ SegyHeader.SweepTraceTaperLenghtEnd = segy_get_bfield_mex( bin, 3245 );
+
+ SegyHeader.TaperType = 0;
+ switch segy_get_bfield_mex( bin, 3247 )
+ case 1
+ SegyHeader.TaperType = 'Linear';
+ case 2
+ SegyHeader.TaperType = 'Cos^2';
+ case 3
+ SegyHeader.TaperType = 'Other';
+ end
+
+ SegyHeader.CorrelatedDataTraces = 0;
+ switch segy_get_bfield_mex( bin, 3249 )
+ case 1
+ SegyHeader.CorrelatedDataTraces = 'No';
+ case 2
+ SegyHeader.CorrelatedDataTraces = 'Yes';
+ end
+
+ SegyHeader.BinaryGainRecovered = 0;
+ switch segy_get_bfield_mex( bin, 3251 )
+ case 1
+ SegyHeader.BinaryGainRecovered = 'Yes';
+ case 2
+ SegyHeader.BinaryGainRecovered = 'No';
+ end
+
+ SegyHeader.AmplitudeRecoveryMethod = 0;
+ switch segy_get_bfield_mex( bin, 3253 )
+ case 1
+ SegyHeader.AmplitudeRecoveryMethod = 'None';
+ case 2
+ SegyHeader.AmplitudeRecoveryMethod = 'SphericalDivergence';
+ case 3
+ SegyHeader.AmplitudeRecoveryMethod = 'AGC';
+ case 4
+ SegyHeader.AmplitudeRecoveryMethod = 'Other';
+ end
+
+ SegyHeader.MeasurementSystem = 0;
+ switch segy_get_bfield_mex( bin, 3255 )
+ case 1
+ SegyHeader.MeasurementSystem = 'Meter';
+ case 2
+ SegyHeader.MeasurementSystem = 'Feet';
+ end
+
+ SegyHeader.ImpulseSignalPolarity = 0;
+ switch segy_get_bfield_mex( bin, 3257 )
+ case 1
+ SegyHeader.ImpulseSignalPolarity = 'IncreasePressureNegativeNumber';
+ case 2
+ SegyHeader.ImpulseSignalPolarity = 'IncreasePressurePositiveNumber';
+ end
+
+ SegyHeader.VibratorPolarityCode = 0;
+ switch segy_get_bfield_mex( bin, 3259 )
+ case 1
+ SegyHeader.VibratorPolarityCode = '337.5-22.5';
+ case 2
+ SegyHeader.VibratorPolarityCode = '22.5-67.5';
+ case 3
+ SegyHeader.VibratorPolarityCode = '67.5-112.5';
+ case 4
+ SegyHeader.VibratorPolarityCode = '112.5-157.5';
+ case 5
+ SegyHeader.VibratorPolarityCode = '157.5-202.5';
+ case 6
+ SegyHeader.VibratorPolarityCode = '202.5-247.5';
+ case 7
+ SegyHeader.VibratorPolarityCode = '247.5-292.5';
+ case 8
+ SegyHeader.VibratorPolarityCode = '292.5-337.5';
+ end
+
+ SegyHeader.FormatRevisionNumber = segy_get_bfield_mex( bin, 3501 );
+ SegyHeader.FixedLengthTraceFlag = segy_get_bfield_mex( bin, 3503 );
+ SegyHeader.NumberOfExtTextHeaders = segy_get_bfield_mex( bin, 3505 );
+ end
+
+ % [tr_heads,notrace] = get_trace_header(filename,itrace);
+ %
+ % Goal:
+ % Read the full trace header of the trace itrace
+ %
+ % Inputs:
+ % filename Filename of segyfile
+ % itrace trace number
+ %
+ % Output:
+ % tr_heads Header values
+ % notrace number of traces in segy file
+ %
+ function [tr_heads, notrace] = get_trace_header(filename, itrace)
+ [header, notrace] = segy_get_trace_header_mex( filename, itrace );
+
+ % read trace header
+ tr_heads.TraceSequenceLine = segy_get_field_mex( header, 1 );
+ tr_heads.TraceSequenceFile = segy_get_field_mex( header, 5 );
+ tr_heads.FieldRecord = segy_get_field_mex( header, 9 );
+ tr_heads.TraceNumber = segy_get_field_mex( header, 13 );
+ tr_heads.EnergySourcePoint = segy_get_field_mex( header, 17 );
+ tr_heads.cdp = segy_get_field_mex( header, 21 );
+ tr_heads.cdpTrace = segy_get_field_mex( header, 25 );
+ tr_heads.TraceIdenitifactionCode = segy_get_field_mex( header, 29 );
+ tr_heads.NSummedTraces = segy_get_field_mex( header, 31 );
+ tr_heads.NStackedTraces = segy_get_field_mex( header, 33 );
+ tr_heads.DataUse = segy_get_field_mex( header, 35 );
+ tr_heads.offset = segy_get_field_mex( header, 37 );
+ tr_heads.ReceiverGroupElevation = segy_get_field_mex( header, 41 );
+ tr_heads.SourceSurfaceElevation = segy_get_field_mex( header, 45 );
+ tr_heads.SourceDepth = segy_get_field_mex( header, 49 );
+ tr_heads.ReceiverDatumElevation = segy_get_field_mex( header, 53 );
+ tr_heads.SourceDatumElevation = segy_get_field_mex( header, 57 );
+ tr_heads.SourceWaterDepth = segy_get_field_mex( header, 61 );
+ tr_heads.GroupWaterDepth = segy_get_field_mex( header, 65 );
+ tr_heads.ElevationScalar = segy_get_field_mex( header, 69 );
+ tr_heads.SourceGroupScalar = segy_get_field_mex( header, 71 );
+ tr_heads.SourceX = segy_get_field_mex( header, 73 );
+ tr_heads.SourceY = segy_get_field_mex( header, 77 );
+ tr_heads.GroupX = segy_get_field_mex( header, 81 );
+ tr_heads.GroupY = segy_get_field_mex( header, 85 );
+ tr_heads.CoordinateUnits = segy_get_field_mex( header, 89 );
+ tr_heads.WeatheringVelocity = segy_get_field_mex( header, 91 );
+ tr_heads.SubWeatheringVelocity = segy_get_field_mex( header, 93 );
+ tr_heads.SourceUpholeTime = segy_get_field_mex( header, 95 );
+ tr_heads.GroupUpholeTime = segy_get_field_mex( header, 97 );
+ tr_heads.SourceStaticCorrection = segy_get_field_mex( header, 99 );
+ tr_heads.GroupStaticCorrection = segy_get_field_mex( header, 101 );
+ tr_heads.TotalStaticApplied = segy_get_field_mex( header, 103 );
+ tr_heads.LagTimeA = segy_get_field_mex( header, 105 );
+ tr_heads.LagTimeB = segy_get_field_mex( header, 107 );
+ tr_heads.DelayRecordingTime = segy_get_field_mex( header, 109 );
+ tr_heads.MuteTimeStart = segy_get_field_mex( header, 111 );
+ tr_heads.MuteTimeEND = segy_get_field_mex( header, 113 );
+ tr_heads.ns = segy_get_field_mex( header, 115 );
+ tr_heads.dt = segy_get_field_mex( header, 117 );
+ tr_heads.GainType = segy_get_field_mex( header, 119 );
+ tr_heads.InstrumentGainConstant = segy_get_field_mex( header, 121 );
+ tr_heads.InstrumentInitialGain = segy_get_field_mex( header, 123 );
+ tr_heads.Correlated = segy_get_field_mex( header, 125 );
+ tr_heads.SweepFrequenceStart = segy_get_field_mex( header, 127 );
+ tr_heads.SweepFrequenceEnd = segy_get_field_mex( header, 129 );
+ tr_heads.SweepLength = segy_get_field_mex( header, 131 );
+ tr_heads.SweepType = segy_get_field_mex( header, 133 );
+ tr_heads.SweepTraceTaperLengthStart = segy_get_field_mex( header, 135 );
+ tr_heads.SweepTraceTaperLengthEnd = segy_get_field_mex( header, 137 );
+ tr_heads.TaperType = segy_get_field_mex( header, 139 );
+ tr_heads.AliasFilterFrequency = segy_get_field_mex( header, 141 );
+ tr_heads.AliasFilterSlope = segy_get_field_mex( header, 143 );
+ tr_heads.NotchFilterFrequency = segy_get_field_mex( header, 145 );
+ tr_heads.NotchFilterSlope = segy_get_field_mex( header, 147 );
+ tr_heads.LowCutFrequency = segy_get_field_mex( header, 149 );
+ tr_heads.HighCutFrequency = segy_get_field_mex( header, 151 );
+ tr_heads.LowCutSlope = segy_get_field_mex( header, 153 );
+ tr_heads.HighCutSlope = segy_get_field_mex( header, 155 );
+ tr_heads.YearDataRecorded = segy_get_field_mex( header, 157 );
+ tr_heads.DayOfYear = segy_get_field_mex( header, 159 );
+ tr_heads.HourOfDay = segy_get_field_mex( header, 161 );
+ tr_heads.MinuteOfHour = segy_get_field_mex( header, 163 );
+ tr_heads.SecondOfMinute = segy_get_field_mex( header, 165 );
+ tr_heads.TimeBaseCode = segy_get_field_mex( header, 167 );
+ tr_heads.TraceWeightningFactor = segy_get_field_mex( header, 169 );
+ tr_heads.GeophoneGroupNumberRoll1 = segy_get_field_mex( header, 171 );
+ tr_heads.GeophoneGroupNumberFirstTraceOrigField = segy_get_field_mex( header, 173 );
+ tr_heads.GeophoneGroupNumberLastTraceOrigField = segy_get_field_mex( header, 175 );
+ tr_heads.GapSize = segy_get_field_mex( header, 177 );
+ tr_heads.OverTravel = segy_get_field_mex( header, 179 );
+ tr_heads.cdpX = segy_get_field_mex( header, 181 );
+ tr_heads.cdpY = segy_get_field_mex( header, 185 );
+ tr_heads.Inline3D = segy_get_field_mex( header, 189 );
+ tr_heads.Crossline3D = segy_get_field_mex( header, 193 );
+ tr_heads.ShotPoint = segy_get_field_mex( header, 197 );
+ tr_heads.ShotPointScalar = segy_get_field_mex( header, 201 );
+ tr_heads.TraceValueMeasurementUnit = segy_get_field_mex( header, 203 );
+ tr_heads.TransductionConstantMantissa = segy_get_field_mex( header, 205 );
+ tr_heads.TransductionConstantPower = segy_get_field_mex( header, 209 );
+ tr_heads.TransductionUnit = segy_get_field_mex( header, 211 );
+ tr_heads.TraceIdentifier = segy_get_field_mex( header, 213 );
+ tr_heads.ScalarTraceHeader = segy_get_field_mex( header, 215 );
+ tr_heads.SourceType = segy_get_field_mex( header, 217 );
+ tr_heads.SourceEnergyDirectionMantissa = segy_get_field_mex( header, 219 );
+ tr_heads.SourceEnergyDirectionExponent = segy_get_field_mex( header, 223 );
+ tr_heads.SourceMeasurementMantissa = segy_get_field_mex( header, 225 );
+ tr_heads.SourceMeasurementExponent = segy_get_field_mex( header, 229 );
+ tr_heads.SourceMeasurementUnit = segy_get_field_mex( header, 231 );
+ tr_heads.UnassignedInt1 = segy_get_field_mex( header, 233 );
+ tr_heads.UnassignedInt2 = segy_get_field_mex( header, 237 );
+ end
+
+ % put_headers(filename,headers,headword)
+ %
+ % Goal:
+ % Fast writing of trace header words in segy file.
+ %
+ % Inputs:
+ % filename Filename of segyfile
+ % headers Array of headervalues or single headervalue. Will be
+ % written to all trace headers. If length of array is
+ % different from number of traces in file an error will be
+ % thrown.
+ % headword Name of header word to be written (example: 'cdp')
+ %
+ function put_headers(filename, headers, headword)
+ ntraces = Segy.get_ntraces( filename );
+ if and( ~isscalar(headers), max(size( headers )) ~= ntraces )
+ error( 'Inconsistent dimensions of header values' )
+ end
+
+ % if single header value, create a traces-sized vector of that
+ % header value, so that segy_put_headers_mex can always assume array
+ if isscalar( headers )
+ headers = ones( 1, ntraces ) * headers;
+ end
+
+ if ischar(headword)
+ headword = TraceField.(headword);
+ end
+
+ segy_put_headers_mex( filename, headers, int32(headword) );
+ end
+ end
+end
diff --git a/mex/SegySampleFormat.m b/mex/SegySampleFormat.m
new file mode 100644
index 0000000..86beca6
--- /dev/null
+++ b/mex/SegySampleFormat.m
@@ -0,0 +1,13 @@
+classdef SegySampleFormat < int32
+% add comment here
+ enumeration
+ IBM_FLOAT_4_BYTE (1)
+ SIGNED_INTEGER_4_BYTE (2)
+ SIGNED_SHORT_2_BYTE (3)
+ FIXED_POINT_WITH_GAIN_4_BYTE (4)
+ IEEE_FLOAT_4_BYTE (5)
+ NOT_IN_USE_1 (6)
+ NOT_IN_USE_2 (7)
+ SIGNED_CHAR_1_BYTE (8)
+ end
+end
diff --git a/mex/SegySpec.m b/mex/SegySpec.m
new file mode 100644
index 0000000..480eed4
--- /dev/null
+++ b/mex/SegySpec.m
@@ -0,0 +1,48 @@
+classdef SegySpec
+% add comment here
+ properties
+ filename
+ sample_format
+ trace_sorting_format
+ sample_indexes
+ crossline_indexes
+ inline_indexes
+ offset_count
+ first_trace_pos
+ il_stride
+ xl_stride
+ trace_bsize
+ t
+ iline
+ xline
+ end
+
+ methods
+
+ function obj = SegySpec(filename, inline_field, crossline_field, t0)
+ spec = segyspec_mex(filename, int32(inline_field), int32(crossline_field), t0);
+ obj.filename = filename;
+
+ if (isempty(spec))
+ e = MException('SegySpec:NoSuchFile', 'File %s not found',filename);
+ throw(e);
+ end
+
+ obj.sample_format = uint32(SegySampleFormat(spec.sample_format));
+ obj.trace_sorting_format = TraceSortingFormat(spec.trace_sorting_format);
+ obj.sample_indexes = spec.sample_indexes;
+ obj.crossline_indexes = uint32(spec.crossline_indexes);
+ obj.inline_indexes = uint32(spec.inline_indexes);
+ obj.offset_count = uint32(spec.offset_count);
+ obj.first_trace_pos = uint32(spec.first_trace_pos);
+ obj.il_stride = spec.il_stride;
+ obj.xl_stride = spec.xl_stride;
+ obj.trace_bsize = spec.trace_bsize;
+ obj.t = obj.sample_indexes;
+ obj.iline = obj.inline_indexes;
+ obj.xline = obj.crossline_indexes;
+ end
+
+ end
+
+end
diff --git a/mex/TraceField.m b/mex/TraceField.m
new file mode 100644
index 0000000..410875a
--- /dev/null
+++ b/mex/TraceField.m
@@ -0,0 +1,96 @@
+classdef TraceField < int32
+ % add comment here
+ enumeration
+ TRACE_SEQUENCE_LINE (1)
+ TRACE_SEQUENCE_FILE (5)
+ FieldRecord (9)
+ TraceNumber (13)
+ EnergySourcePoint (17)
+ CDP (21)
+ CDP_TRACE (25)
+ TraceIdentificationCode (29)
+ NSummedTraces (31)
+ NStackedTraces (33)
+ DataUse (35)
+ offset (37)
+ ReceiverGroupElevation (41)
+ SourceSurfaceElevation (45)
+ SourceDepth (49)
+ ReceiverDatumElevation (53)
+ SourceDatumElevation (57)
+ SourceWaterDepth (61)
+ GroupWaterDepth (65)
+ ElevationScalar (69)
+ SourceGroupScalar (71)
+ SourceX (73)
+ SourceY (77)
+ GroupX (81)
+ GroupY (85)
+ CoordinateUnits (89)
+ WeatheringVelocity (91)
+ SubWeatheringVelocity (93)
+ SourceUpholeTime (95)
+ GroupUpholeTime (97)
+ SourceStaticCorrection (99)
+ GroupStaticCorrection (101)
+ TotalStaticApplied (103)
+ LagTimeA (105)
+ LagTimeB (107)
+ DelayRecordingTime (109)
+ MuteTimeStart (111)
+ MuteTimeEND (113)
+ TRACE_SAMPLE_COUNT (115)
+ TRACE_SAMPLE_INTERVAL (117)
+ GainType (119)
+ InstrumentGainConstant (121)
+ InstrumentInitialGain (123)
+ Correlated (125)
+ SweepFrequencyStart (127)
+ SweepFrequencyEnd (129)
+ SweepLength (131)
+ SweepType (133)
+ SweepTraceTaperLengthStart (135)
+ SweepTraceTaperLengthEnd (137)
+ TaperType (139)
+ AliasFilterFrequency (141)
+ AliasFilterSlope (143)
+ NotchFilterFrequency (145)
+ NotchFilterSlope (147)
+ LowCutFrequency (149)
+ HighCutFrequency (151)
+ LowCutSlope (153)
+ HighCutSlope (155)
+ YearDataRecorded (157)
+ DayOfYear (159)
+ HourOfDay (161)
+ MinuteOfHour (163)
+ SecondOfMinute (165)
+ TimeBaseCode (167)
+ TraceWeightingFactor (169)
+ GeophoneGroupNumberRoll1 (171)
+ GeophoneGroupNumberFirstTraceOrigField (173)
+ GeophoneGroupNumberLastTraceOrigField (175)
+ GapSize (177)
+ OverTravel (179)
+ CDP_X (181)
+ CDP_Y (185)
+ INLINE_3D (189)
+ CROSSLINE_3D (193)
+ ShotPoint (197)
+ ShotPointScalar (201)
+ TraceValueMeasurementUnit (203)
+ TransductionConstantMantissa (205)
+ TransductionConstantPower (209)
+ TransductionUnit (211)
+ TraceIdentifier (213)
+ ScalarTraceHeader (215)
+ SourceType (217)
+ SourceEnergyDirectionMantissa (219)
+ SourceEnergyDirectionExponent (223)
+ SourceMeasurementMantissa (225)
+ SourceMeasurementExponent (229)
+ SourceMeasurementUnit (231)
+ UnassignedInt1 (233)
+ UnassignedInt2 (237)
+ end
+end
\ No newline at end of file
diff --git a/mex/TraceSortingFormat.m b/mex/TraceSortingFormat.m
new file mode 100644
index 0000000..f26a250
--- /dev/null
+++ b/mex/TraceSortingFormat.m
@@ -0,0 +1,9 @@
+classdef TraceSortingFormat < int32
+% add comment here
+ enumeration
+ UNKNOWN_SORTING (0)
+ XLINE (1)
+ INLINE (2)
+ end
+end
+
diff --git a/mex/get_line.c b/mex/get_line.c
new file mode 100644
index 0000000..173477d
--- /dev/null
+++ b/mex/get_line.c
@@ -0,0 +1,41 @@
+#include <segyio/segy.h>
+
+#include "mex.h"
+
+/*
+ * get_line.c
+ *
+ * get_line( cube, dir, n )
+ */
+
+void mexFunction( int nlhs, mxArray* plhs[],
+ int nrhs, mxArray* prhs[] ) {
+
+ if( nhrs != 3 ) {
+ mxErrMsgIdAndTxt("segy:get_line:nrhs", "3 input arguments required: cube, dir, n");
+ }
+
+ if( nlhs != 1 ) {
+ mxErrMsgIdAndTxt("segy:get_line:nrhs", "1 arguments required: line");
+ }
+
+ int err;
+ const mxArray* cube = prhs[0];
+ const mxArray* dir = prhs[1];
+ const mxArray* n = prhs[2];
+
+ const size_t dir_arg_size = sizeof( "iline" );
+ char* dir_c = malloc( dir_arg_size );
+ err = mxGetString( dir, dir_c dir_arg_size );
+
+ if( err != 0 ) {
+ mxErrMsgIdAndTxt("segy:get_line:strcpy", "Failure parsing direction argument" );
+ }
+
+ if( strncmp( dir_c, "iline", dir_arg_size ) != 0
+ && strncmp( dir_c, "xline", dir_arg_size ) ) {
+ mxErrMsgIdAndTxt("segy:get_line:dir", "Invalid direction. Valid directions: 'iline', 'xline'");
+ }
+ const bool iline = strncmp( dir_c, "iline", dir_arg_size ) == 0;
+
+}
diff --git a/mex/segy_get_bfield_mex.c b/mex/segy_get_bfield_mex.c
new file mode 100644
index 0000000..6d724f5
--- /dev/null
+++ b/mex/segy_get_bfield_mex.c
@@ -0,0 +1,24 @@
+#include <errno.h>
+#include <string.h>
+
+#include <segyio/segy.h>
+#include "segyutil.h"
+
+#include "matrix.h"
+#include "mex.h"
+
+void mexFunction(int nlhs, mxArray *plhs[],
+ int nrhs, const mxArray *prhs[]) {
+
+ const char* bin = mxGetData( prhs[ 0 ] );
+ const int field = mxGetScalar( prhs[ 1 ] );
+ int f;
+
+ int err = segy_get_bfield( bin, field, &f );
+
+ if( err == SEGY_INVALID_FIELD )
+ mexErrMsgIdAndTxt( "segy:get_bfield:invalid_field",
+ "Invalid field value/header offset" );
+
+ plhs[ 0 ] = mxCreateDoubleScalar( f );
+}
diff --git a/mex/segy_get_field_mex.c b/mex/segy_get_field_mex.c
new file mode 100644
index 0000000..fd243f1
--- /dev/null
+++ b/mex/segy_get_field_mex.c
@@ -0,0 +1,24 @@
+#include <errno.h>
+#include <string.h>
+
+#include <segyio/segy.h>
+#include "segyutil.h"
+
+#include "matrix.h"
+#include "mex.h"
+
+void mexFunction(int nlhs, mxArray *plhs[],
+ int nrhs, const mxArray *prhs[]) {
+
+ const char* bin = mxGetData( prhs[ 0 ] );
+ const int field = mxGetScalar( prhs[ 1 ] );
+ int f;
+
+ int err = segy_get_field( bin, field, &f );
+
+ if( err == SEGY_INVALID_FIELD )
+ mexErrMsgIdAndTxt( "segy:get_field:invalid_field",
+ "Invalid field value/header offset" );
+
+ plhs[ 0 ] = mxCreateDoubleScalar( f );
+}
diff --git a/mex/segy_get_header_mex.c b/mex/segy_get_header_mex.c
new file mode 100644
index 0000000..8cf3808
--- /dev/null
+++ b/mex/segy_get_header_mex.c
@@ -0,0 +1,61 @@
+#include <errno.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <segyio/segy.h>
+#include "segyutil.h"
+
+#include "matrix.h"
+#include "mex.h"
+
+void mexFunction(int nlhs, mxArray *plhs[],
+ int nrhs, const mxArray *prhs[]) {
+
+ char* msg1;
+ char* msg2;
+ int err;
+
+ const char* filename = mxArrayToString( prhs[ 0 ] );
+ FILE* fp = segyfopen( prhs[ 0 ], "r" );
+
+ int field = mxGetScalar( prhs[ 1 ] );
+
+ struct segy_file_format fmt = filefmt( fp );
+
+ plhs[0] = mxCreateNumericMatrix( 1, fmt.traces, mxINT32_CLASS, mxREAL );
+ int32_t* out = mxGetData( plhs[ 0 ] );
+
+ char header[ SEGY_TRACE_HEADER_SIZE ];
+
+ for( size_t i = 0; i < fmt.traces; ++i ) {
+ int32_t f;
+ err = segy_traceheader( fp, i, header, fmt.trace0, fmt.trace_bsize );
+
+ if( err != 0 ) {
+ msg1 = "segy:get_header:segy_traceheader";
+ msg2 = strerror( errno );
+ goto cleanup;
+ }
+
+ err = segy_get_field( header, field, &f );
+
+ if( err != 0 ) {
+ msg1 = "segy:get_header:segy_get_field";
+ msg2 = "Error reading header field.";
+ goto cleanup;
+ }
+
+ out[ i ] = f;
+ }
+
+ fclose( fp );
+
+ plhs[ 1 ] = mxCreateDoubleScalar( fmt.traces );
+ return;
+
+cleanup:
+ fclose( fp );
+
+cleanup_fopen:
+ mexErrMsgIdAndTxt( msg1, msg2 );
+}
diff --git a/mex/segy_get_ntraces_mex.c b/mex/segy_get_ntraces_mex.c
new file mode 100644
index 0000000..7f35172
--- /dev/null
+++ b/mex/segy_get_ntraces_mex.c
@@ -0,0 +1,14 @@
+#include <segyio/segy.h>
+#include "segyutil.h"
+
+#include "mex.h"
+
+void mexFunction(int nlhs, mxArray *plhs[],
+ int nrhs, const mxArray *prhs[]) {
+
+ FILE* fp = segyfopen( prhs[ 0 ], "r" );
+ struct segy_file_format fmt = filefmt( fp );
+ fclose( fp );
+
+ plhs[0] = mxCreateDoubleScalar( fmt.traces );
+}
diff --git a/mex/segy_get_segy_header_mex.c b/mex/segy_get_segy_header_mex.c
new file mode 100644
index 0000000..d804074
--- /dev/null
+++ b/mex/segy_get_segy_header_mex.c
@@ -0,0 +1,44 @@
+#include <errno.h>
+#include <string.h>
+
+#include <segyio/segy.h>
+#include "segyutil.h"
+
+#include "matrix.h"
+#include "mex.h"
+
+void mexFunction(int nlhs, mxArray *plhs[],
+ int nrhs, const mxArray *prhs[]) {
+
+ char* msg1;
+ char* msg2;
+ int err;
+
+ FILE* fp = segyfopen( prhs[ 0 ], "r" );
+ char* textheader = mxMalloc( segy_textheader_size() );
+ err = segy_textheader( fp, textheader );
+
+ if( err != 0 ) {
+ msg1 = "segy:text_header:os";
+ msg2 = strerror( errno );
+ goto cleanup;
+ }
+ plhs[ 0 ] = mxCreateString( textheader );
+
+ mwSize dims[ 1 ] = { segy_binheader_size() };
+ plhs[ 1 ] = mxCreateCharArray( 1, dims );
+ err = segy_binheader( fp, mxGetData( plhs[ 1 ] ) );
+
+ if( err != 0 ) {
+ msg1 = "segy:binary_header:os";
+ msg2 = strerror( errno );
+ goto cleanup;
+ }
+
+ mxFree( textheader );
+ return;
+
+cleanup:
+ fclose( fp );
+ mexErrMsgIdAndTxt( msg1, msg2 );
+}
diff --git a/mex/segy_get_trace_header_mex.c b/mex/segy_get_trace_header_mex.c
new file mode 100644
index 0000000..d46bebd
--- /dev/null
+++ b/mex/segy_get_trace_header_mex.c
@@ -0,0 +1,42 @@
+#include <errno.h>
+#include <string.h>
+
+#include <segyio/segy.h>
+#include "segyutil.h"
+
+#include "matrix.h"
+#include "mex.h"
+
+void mexFunction(int nlhs, mxArray *plhs[],
+ int nrhs, const mxArray *prhs[]) {
+
+ char* msg1;
+ char* msg2;
+ int err;
+
+ FILE* fp = segyfopen( prhs[ 0 ], "r" );
+ struct segy_file_format fmt = filefmt( fp );
+ int traceno = mxGetScalar( prhs[ 1 ] );
+
+ if( traceno > fmt.traces )
+ mexErrMsgIdAndTxt( "segy:get_trace_header:bounds",
+ "Requested trace header does not exist in this file." );
+
+ mwSize dims[ 1 ] = { SEGY_TRACE_HEADER_SIZE };
+ plhs[ 0 ] = mxCreateCharArray( 1, dims );
+ err = segy_traceheader( fp, traceno, mxGetData( plhs[ 0 ] ), fmt.trace0, fmt.trace_bsize );
+
+ fclose( fp );
+
+ if( err != 0 ) {
+ msg1 = "segy:get_trace_header:os";
+ msg2 = strerror( errno );
+ goto cleanup;
+ }
+
+ plhs[ 1 ] = mxCreateDoubleScalar( fmt.traces );
+ return;
+
+cleanup:
+ mexErrMsgIdAndTxt( msg1, msg2 );
+}
diff --git a/mex/segy_get_traces_mex.c b/mex/segy_get_traces_mex.c
new file mode 100644
index 0000000..7f04589
--- /dev/null
+++ b/mex/segy_get_traces_mex.c
@@ -0,0 +1,76 @@
+#include <errno.h>
+#include <string.h>
+
+#include <segyio/segy.h>
+#include "segyutil.h"
+
+#include "matrix.h"
+#include "mex.h"
+
+void mexFunction(int nlhs, mxArray *plhs[],
+ int nrhs, const mxArray *prhs[]) {
+
+ char* msg1;
+ char* msg2;
+ int err;
+
+ FILE* fp = segyfopen( prhs[ 0 ], "r" );
+ int first_trace = mxGetScalar( prhs[ 1 ] );
+ int last_trace = mxGetScalar( prhs[ 2 ] );
+ int notype = mxGetScalar( prhs[ 3 ] );
+
+ struct segy_file_format fmt = filefmt( fp );
+
+ char binary[ SEGY_BINARY_HEADER_SIZE ];
+ err = segy_binheader( fp, binary );
+ if( err != 0 ) {
+ msg1 = "segy:get_traces:binary";
+ msg2 = strerror( errno );
+ goto cleanup;
+ }
+
+ if( last_trace != -1 && last_trace < fmt.traces )
+ fmt.traces = (last_trace + 1);
+
+ fmt.traces -= first_trace;
+
+ plhs[0] = mxCreateNumericMatrix( fmt.samples, fmt.traces, mxSINGLE_CLASS, mxREAL );
+ float* out = mxGetData( plhs[ 0 ] );
+
+ if( first_trace > fmt.traces ) {
+ msg1 = "segy:get_traces:bounds";
+ msg2 = "first trace must be smaller than last trace";
+ goto cleanup;
+ }
+
+ for( size_t i = first_trace; i < fmt.traces; ++i ) {
+ err = segy_readtrace( fp, i, out, fmt.trace0, fmt.trace_bsize );
+ out += fmt.samples;
+
+ if( err != 0 ) {
+ msg1 = "segy:get_traces:segy_readtrace";
+ msg2 = strerror( errno );
+ goto cleanup;
+ }
+ }
+
+ fclose( fp );
+
+ if( notype != -1 )
+ fmt.format = notype;
+
+ segy_to_native( fmt.format, fmt.samples * fmt.traces, mxGetData( plhs[ 0 ] ) );
+
+ int interval;
+ segy_get_bfield( binary, BIN_Interval, &interval );
+ plhs[ 1 ] = mxCreateDoubleScalar( interval );
+ plhs[ 2 ] = mxCreateDoubleScalar( fmt.format );
+
+ return;
+
+cleanup:
+ fclose( fp );
+
+cleanup_fopen:
+ mexErrMsgIdAndTxt( msg1, msg2 );
+}
diff --git a/mex/segy_interpret_segycube_mex.c b/mex/segy_interpret_segycube_mex.c
new file mode 100644
index 0000000..bc8d829
--- /dev/null
+++ b/mex/segy_interpret_segycube_mex.c
@@ -0,0 +1,8 @@
+#include <errno.h>
+#include <string.h>
+
+#include <segyio/segy.h>
+#include "segyutil.h"
+
+#include "matrix.h"
+#include "mex.h"
diff --git a/mex/segy_put_headers_mex.c b/mex/segy_put_headers_mex.c
new file mode 100644
index 0000000..abba4db
--- /dev/null
+++ b/mex/segy_put_headers_mex.c
@@ -0,0 +1,62 @@
+#include <errno.h>
+#include <string.h>
+
+#include <segyio/segy.h>
+#include "segyutil.h"
+
+#include "matrix.h"
+#include "mex.h"
+
+void mexFunction(int nlhs, mxArray *plhs[],
+ int nrhs, const mxArray *prhs[]) {
+
+ char* msg1;
+ char* msg2;
+ int err;
+
+ FILE* fp = segyfopen( prhs[ 0 ], "r+" );
+ double* headers = mxGetPr( prhs[ 1 ] );
+ int field = mxGetScalar( prhs[ 2 ] );
+
+ struct segy_file_format fmt = filefmt( fp );
+
+ char traceheader[ SEGY_TRACE_HEADER_SIZE ];
+ /*
+ * check that the field is valid and writing it won't return an error. by
+ * checking it here we don't have to do it in the write loop
+ */
+ err = segy_set_field( traceheader, field, 0 );
+
+ if( err != 0 ) {
+ msg1 = "segy:put_headers:invalid_field";
+ msg2 = "Invalid field value/header offset";
+ goto cleanup;
+ }
+
+ double* itr = headers;
+ for( size_t i = 0; i < fmt.traces; ++i, ++itr ) {
+ err = segy_traceheader( fp, i, traceheader, fmt.trace0, fmt.trace_bsize );
+ const int val = *itr;
+
+ if( err != 0 ) {
+ msg1 = "segy:put_headers:os";
+ msg2 = strerror( errno );
+ goto cleanup;
+ }
+
+ segy_set_field( traceheader, field, val );
+ err = segy_write_traceheader( fp, i, traceheader, fmt.trace0, fmt.trace_bsize );
+ if( err != 0 ) {
+ msg1 = "segy:put_headers:os";
+ msg2 = strerror( errno );
+ goto cleanup;
+ }
+ }
+
+ fclose( fp );
+ return;
+
+cleanup:
+ fclose( fp );
+ mexErrMsgIdAndTxt( msg1, msg2 );
+}
diff --git a/mex/segy_put_traces_mex.c b/mex/segy_put_traces_mex.c
new file mode 100644
index 0000000..df317dd
--- /dev/null
+++ b/mex/segy_put_traces_mex.c
@@ -0,0 +1,69 @@
+#include <errno.h>
+#include <string.h>
+
+#include <segyio/segy.h>
+#include "segyutil.h"
+
+#include "matrix.h"
+#include "mex.h"
+
+void mexFunction(int nlhs, mxArray *plhs[],
+ int nrhs, const mxArray *prhs[]) {
+
+ char* msg1;
+ char* msg2;
+ int err;
+
+ FILE* fp = segyfopen( prhs[ 0 ], "r+" );
+ plhs[ 0 ] = mxDuplicateArray( prhs[ 1 ] );
+ int first_trace = mxGetScalar( prhs[ 2 ] );
+ int last_trace = mxGetScalar( prhs[ 3 ] );
+ int notype = mxGetScalar( prhs[ 4 ] );
+
+ char binary[ SEGY_BINARY_HEADER_SIZE ];
+ struct segy_file_format fmt = filefmt( fp );
+
+ if( notype != -1 )
+ fmt.format = notype;
+
+ if( last_trace != 1 && last_trace < fmt.traces )
+ fmt.traces = (last_trace + 1);
+
+ fmt.traces -= first_trace;
+
+ float* out = mxGetData( plhs[ 0 ] );
+ segy_from_native( fmt.format, fmt.samples * fmt.traces, out );
+
+ if( first_trace > fmt.traces ) {
+ msg1 = "segy:get_traces:bounds";
+ msg2 = "first trace must be smaller than last trace";
+ goto cleanup;
+ }
+
+ float* itr = out;
+ for( size_t i = first_trace; i < fmt.traces; ++i ) {
+ err = segy_writetrace( fp, i, itr, fmt.trace0, fmt.trace_bsize );
+ itr += fmt.samples;
+
+ if( err != 0 ) {
+ msg1 = "segy:put_traces:segy_writetrace";
+ msg2 = strerror( errno );
+ fmt.traces = i;
+ goto cleanup;
+ }
+ }
+
+ fclose( fp );
+
+ segy_to_native( fmt.format, fmt.samples * fmt.traces, out );
+
+ plhs[ 1 ] = mxCreateDoubleScalar( fmt.format );
+
+ return;
+
+cleanup:
+ fclose( fp );
+ segy_to_native( fmt.format, fmt.samples * fmt.traces, out );
+
+ mexErrMsgIdAndTxt( msg1, msg2 );
+}
diff --git a/mex/segy_read_write_line_mex.c b/mex/segy_read_write_line_mex.c
new file mode 100644
index 0000000..5bbf9a2
--- /dev/null
+++ b/mex/segy_read_write_line_mex.c
@@ -0,0 +1,114 @@
+#include <string.h>
+#include "mex.h"
+#include "matrix.h"
+
+#include "segyutil.h"
+
+#include <segyio/segy.h>
+
+/* The gateway function */
+void mexFunction(int nlhs, mxArray *plhs[],
+ int nrhs, const mxArray *prhs[]) {
+
+ bool read;
+
+ if (nrhs == 5) {
+ read = true;
+ }
+ else if (nrhs == 6) {
+ read = false;
+ }
+ else {
+ goto ERROR;
+ }
+
+ const mxArray* mx_spec = prhs[0];
+ const mxArray* mx_index = prhs[1];
+ const mxArray* mx_line_length = prhs[2];
+ const mxArray* mx_line_indexes = prhs[3];
+ const mxArray* mx_stride = prhs[4];
+
+ SegySpec spec;
+ recreateSpec(&spec,mx_spec);
+
+ size_t index = (size_t)mxGetScalar(mx_index);
+
+ uint32_t line_length = (uint32_t)mxGetScalar(mx_line_length);
+
+ uint32_t* line_indexes = (uint32_t*)mxGetData(mx_line_indexes);
+ int n = mxGetN(mx_line_indexes);
+ int m = mxGetM(mx_line_indexes);
+ uint32_t line_count = (n>m)? n:m;
+
+ uint32_t stride = (uint32_t)mxGetScalar(mx_stride);
+
+ FILE* fp;
+
+ unsigned int line_trace0;
+
+ int errc = segy_line_trace0( index, line_length, stride, line_indexes, line_count, &line_trace0 );
+ if (errc != 0) {
+ goto CLEANUP;
+ }
+
+ if (read) {
+ fp = fopen( spec.filename, "r" );
+ if (fp == NULL) {
+ goto CLEANUP;
+ }
+
+ plhs[0] = mxCreateNumericMatrix(spec.sample_count, line_length, mxSINGLE_CLASS, mxREAL);
+ float *data_ptr = (float *) mxGetData(plhs[0]);
+
+ errc = segy_read_line( fp, line_trace0, line_length, stride, data_ptr, spec.first_trace_pos, spec.trace_bsize );
+ if (errc != 0) {
+ goto CLEANUP;
+ }
+
+ errc = segy_to_native( spec.sample_format, line_length * spec.sample_count, data_ptr );
+ if (errc != 0) {
+ goto CLEANUP;
+ }
+ }
+ else {
+ fp = fopen( spec.filename, "r+" );
+ if (fp == NULL) {
+ goto CLEANUP;
+ }
+
+ const mxArray* mx_data = prhs[5];
+
+ float *data_ptr = (float *) mxGetData(mx_data);
+
+ errc = segy_from_native( spec.sample_format, line_length * spec.sample_count, data_ptr );
+ if (errc != 0) {
+ goto CLEANUP;
+ }
+
+ errc = segy_write_line( fp, line_trace0, line_length, stride, data_ptr, spec.first_trace_pos, spec.trace_bsize );
+ if (errc != 0) {
+ goto CLEANUP;
+ }
+
+ errc = segy_to_native( spec.sample_format, line_length * spec.sample_count, data_ptr );
+ if (errc != 0) {
+ goto CLEANUP;
+ }
+ }
+
+ fclose(fp);
+ return;
+
+ CLEANUP:
+ fclose(fp);
+ ERROR:
+ {
+ int nfields = 1;
+ const char *fnames[nfields];
+ fnames[0] = "error";
+ plhs[0] = mxCreateStructMatrix(0,0, nfields, fnames);
+ mxSetFieldByNumber(plhs[0], 0, 0, mxCreateDoubleScalar(errc));
+ }
+
+
+}
diff --git a/mex/segyspec_mex.c b/mex/segyspec_mex.c
new file mode 100644
index 0000000..72f3e1e
--- /dev/null
+++ b/mex/segyspec_mex.c
@@ -0,0 +1,148 @@
+#include <string.h>
+#include "mex.h"
+
+#include <../src/segyio/segy.h>
+#include "segyutil.h"
+
+mxArray *createPLHSStruct() {
+ int nfields = 11;
+ const char *fnames[nfields];
+
+ fnames[0] = "filename";
+ fnames[1] = "sample_format";
+ fnames[2] = "crossline_indexes";
+ fnames[3] = "inline_indexes";
+ fnames[4] = "sample_indexes";
+ fnames[5] = "trace_sorting_format";
+ fnames[6] = "offset_count";
+ fnames[7] = "first_trace_pos";
+ fnames[8] = "il_stride";
+ fnames[9] = "xl_stride";
+ fnames[10] = "trace_bsize";
+
+ mxArray *plhs = mxCreateStructMatrix(1,1,nfields,fnames);
+
+ return plhs;
+}
+
+
+void checkInputOutputSizes(int nlhs, int nrhs ) {
+
+ /* check for proper number of arguments */
+ if(nrhs!=4) {
+ mexErrMsgIdAndTxt("MyToolbox:arrayProduct:nrhs","Four inputs required.");
+ }
+ if(nlhs!=1) {
+ mexErrMsgIdAndTxt("MyToolbox:arrayProduct:nlhs","One output required.");
+ }
+
+}
+
+void checkInputOutput(int nlhs, mxArray **plhs,
+ int nrhs, const mxArray **prhs) {
+
+ checkInputOutputSizes(nlhs, nrhs);
+
+ /* First input must be a string */
+ if ( mxIsChar(prhs[0]) != 1)
+ mexErrMsgIdAndTxt( "SegyIo:segyspec:inputNotString",
+ "Input must be a string.");
+
+ /* First input must be a row vector */
+ if (mxGetM(prhs[0])!=1)
+ mexErrMsgIdAndTxt( "SegyIo:segyspec:inputNotVector",
+ "Input must be a row vector.");
+
+ /* make sure the second input argument is int */
+ if( !mxIsNumeric(prhs[1]) ||
+ mxGetNumberOfElements(prhs[1])!=1 ) {
+ mexErrMsgIdAndTxt("SegyIo:segyspec:notScalar","Input multiplier must be a numeric.");
+ }
+
+ /* make sure the third input argument is int */
+ if( !mxIsNumeric(prhs[2]) ||
+ mxGetNumberOfElements(prhs[2])!=1 ) {
+ mexErrMsgIdAndTxt("SegyIo:segyspec:notScalar","Input multiplier must be a int16.");
+ }
+
+ /* make sure the fourth input argument is double */
+ if( !mxIsDouble(prhs[3]) ||
+ mxGetNumberOfElements(prhs[3])!=1 ) {
+ mexErrMsgIdAndTxt("SegyIo:segyspec:notScalar","Input multiplier must be a double.");
+ }
+
+
+}
+
+/* The gateway function */
+void mexFunction( int nlhs, mxArray *plhs[],
+ int nrhs, const mxArray *prhs[]) {
+
+ plhs[0] = createPLHSStruct();
+
+ checkInputOutput(nlhs, plhs, nrhs, prhs);
+ char *filename = mxArrayToString(prhs[0]);
+
+ int il = (int)mxGetScalar(prhs[1]);
+ int xl = (int)mxGetScalar(prhs[2]);
+ float t0 = (float)mxGetScalar(prhs[3]);
+
+ SegySpec spec;
+ int errc = segyCreateSpec(&spec, filename, il, xl, t0);
+ if (errc != 0) {
+ goto FAILURE;
+ }
+ mxSetFieldByNumber(plhs[0], 0, 0, mxCreateString(spec.filename));
+
+ mxSetFieldByNumber(plhs[0], 0, 1, mxCreateDoubleScalar(spec.sample_format));
+
+ mxArray *crossline_indexes = mxCreateDoubleMatrix(spec.crossline_count, 1, mxREAL);
+ double *crossline_indexes_ptr = mxGetPr(crossline_indexes);
+ for (int i = 0; i < spec.crossline_count; i++) {
+ crossline_indexes_ptr[i] = spec.crossline_indexes[i];
+ }
+ mxSetFieldByNumber(plhs[0], 0, 2, crossline_indexes);
+
+ mxArray *inline_indexes = mxCreateDoubleMatrix(spec.inline_count, 1, mxREAL);
+ double *inline_indexes_ptr = mxGetPr(inline_indexes);
+ for (int i = 0; i < spec.inline_count; i++) {
+ inline_indexes_ptr[i] = spec.inline_indexes[i];
+ }
+ mxSetFieldByNumber(plhs[0], 0, 3, inline_indexes);
+
+ mxArray *mx_sample_indexes = mxCreateDoubleMatrix(spec.sample_count,1, mxREAL);
+ double *mx_sample_indexes_ptr = mxGetPr(mx_sample_indexes);
+ for (int i = 0; i < spec.sample_count; i++) {
+ mx_sample_indexes_ptr[i] = spec.sample_indexes[i];
+ }
+ mxSetFieldByNumber(plhs[0], 0, 4, mx_sample_indexes);
+
+ mxSetFieldByNumber(plhs[0], 0, 5, mxCreateDoubleScalar(spec.trace_sorting_format));
+ mxSetFieldByNumber(plhs[0], 0, 6, mxCreateDoubleScalar(spec.offset_count));
+ mxSetFieldByNumber(plhs[0], 0, 7, mxCreateDoubleScalar(spec.first_trace_pos));
+ mxSetFieldByNumber(plhs[0], 0, 8, mxCreateDoubleScalar(spec.il_stride));
+ mxSetFieldByNumber(plhs[0], 0, 9, mxCreateDoubleScalar(spec.xl_stride));
+ mxSetFieldByNumber(plhs[0], 0, 10, mxCreateDoubleScalar(spec.trace_bsize));
+
+ if (spec.crossline_indexes != NULL)
+ free(spec.crossline_indexes);
+ if (spec.inline_indexes != NULL)
+ free(spec.inline_indexes);
+ if (spec.sample_indexes != NULL)
+ free(spec.sample_indexes);
+ if (spec.filename != NULL)
+ free(spec.filename);
+ mxFree(filename);
+
+ return;
+
+ FAILURE:
+ {
+ int nfields = 1;
+ const char *fnames[nfields];
+ fnames[0] = "error";
+ plhs[0] = mxCreateStructMatrix(0,0, nfields, fnames);
+ }
+ mxFree(filename);
+
+}
diff --git a/mex/segyutil.c b/mex/segyutil.c
new file mode 100644
index 0000000..313ff5d
--- /dev/null
+++ b/mex/segyutil.c
@@ -0,0 +1,89 @@
+#include <errno.h>
+#include <malloc.h>
+#include <string.h>
+
+#include "segyio/segy.h"
+#include "segyutil.h"
+
+static int getMaxDim(mxArray* arr){
+ int n = mxGetN(arr);
+ int m = mxGetM(arr);
+
+ int max = m;
+ if (n>m) max = n;
+
+ return max;
+}
+
+void recreateSpec(SegySpec *spec, const mxArray* mex_spec) {
+
+ spec->filename = mxArrayToString(mxGetProperty(mex_spec, 0, "filename"));
+ spec->sample_format = (unsigned int)mxGetScalar(mxGetProperty(mex_spec, 0, "sample_format"));
+ spec->trace_sorting_format = (unsigned int)mxGetScalar(mxGetProperty(mex_spec, 0, "trace_sorting_format"));
+ spec->offset_count = (unsigned int)mxGetScalar(mxGetProperty(mex_spec, 0, "offset_count"));
+ spec->first_trace_pos = (unsigned int)mxGetScalar(mxGetProperty(mex_spec, 0, "first_trace_pos"));
+ spec->il_stride = (unsigned int)mxGetScalar(mxGetProperty(mex_spec, 0, "il_stride"));
+ spec->xl_stride = (unsigned int)mxGetScalar(mxGetProperty(mex_spec, 0, "xl_stride"));
+ spec->trace_bsize = (unsigned int)mxGetScalar(mxGetProperty(mex_spec, 0, "trace_bsize"));
+
+ mxArray* crossline_indexes = mxGetProperty(mex_spec, 0, "crossline_indexes");
+ spec->crossline_count = getMaxDim(crossline_indexes);
+ spec->crossline_indexes = mxGetData(crossline_indexes);
+
+ mxArray* inline_indexes = mxGetProperty(mex_spec, 0, "inline_indexes");
+ spec->inline_count = getMaxDim(inline_indexes);
+ spec->inline_indexes = mxGetData(inline_indexes);
+
+ mxArray* sample_indexes = mxGetProperty(mex_spec, 0, "sample_indexes");
+ spec->sample_count = getMaxDim(sample_indexes);
+ spec->sample_indexes = mxGetData(sample_indexes);
+
+}
+
+struct segy_file_format buffmt( const char* binary ) {
+ struct segy_file_format fmt;
+ fmt.samples = segy_samples( binary );
+ fmt.trace_bsize = segy_trace_bsize( fmt.samples );
+ fmt.trace0 = segy_trace0( binary );
+ fmt.format = segy_format( binary );
+ fmt.traces = 0;
+
+ return fmt;
+}
+
+struct segy_file_format filefmt( FILE* fp ) {
+ char binary[SEGY_BINARY_HEADER_SIZE];
+ int err = segy_binheader( fp, binary );
+
+ if( err != 0 )
+ mexErrMsgIdAndTxt( "segy:c:filemft", strerror( errno ) );
+
+ struct segy_file_format fmt = buffmt( binary );
+
+ err = segy_traces( fp, &fmt.traces, fmt.trace0, fmt.trace_bsize );
+ if( err == 0 ) return fmt;
+
+ const char* msg1 = "segy:c:filefmt";
+ const char* msg2;
+
+ if( err == SEGY_TRACE_SIZE_MISMATCH )
+ msg2 = "Number of traces not consistent with file size. File corrupt?";
+ else
+ msg2 = strerror( errno );
+
+ mexErrMsgIdAndTxt( msg1, msg2 );
+}
+
+FILE* segyfopen( const mxArray* filename, const char* mode ) {
+ const char* fname = mxArrayToString( filename );
+
+ FILE* fp = fopen( fname, mode );
+ int err = errno;
+
+ mxFree( (void*)fname );
+
+ if( !fp )
+ mexErrMsgIdAndTxt( "segy:c:fopen", strerror( errno ) );
+
+ return fp;
+}
diff --git a/mex/segyutil.h b/mex/segyutil.h
new file mode 100644
index 0000000..2b006ab
--- /dev/null
+++ b/mex/segyutil.h
@@ -0,0 +1,26 @@
+#ifndef SEGYIO_SEGYUTIL_H
+#define SEGYIO_SEGYUTIL_H
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <mex.h>
+
+#include "spec/segyspec.h"
+
+
+void recreateSpec(SegySpec* spec, const mxArray* mex_spec);
+
+struct segy_file_format {
+ unsigned int samples;
+ long trace0;
+ unsigned int trace_bsize;
+ size_t traces;
+ int format;
+};
+
+struct segy_file_format buffmt( const char* );
+struct segy_file_format filefmt( FILE* );
+FILE* segyfopen( const mxArray* filename, const char* mode );
+
+#endif //SEGYIO_SEGYUTIL_H
diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt
new file mode 100644
index 0000000..adbf33a
--- /dev/null
+++ b/python/CMakeLists.txt
@@ -0,0 +1,2 @@
+add_subdirectory(cwrap)
+add_subdirectory(segyio)
\ No newline at end of file
diff --git a/python/cwrap/CMakeLists.txt b/python/cwrap/CMakeLists.txt
new file mode 100644
index 0000000..4f70026
--- /dev/null
+++ b/python/cwrap/CMakeLists.txt
@@ -0,0 +1,10 @@
+set(PYTHON_SOURCES
+ __init__.py
+ basecclass.py
+ basecenum.py
+ basecvalue.py
+ metacwrap.py
+ prototype.py
+)
+
+add_python_package(cwrap cwrap "${PYTHON_SOURCES}")
\ No newline at end of file
diff --git a/python/cwrap/__init__.py b/python/cwrap/__init__.py
new file mode 100644
index 0000000..788249c
--- /dev/null
+++ b/python/cwrap/__init__.py
@@ -0,0 +1,5 @@
+from .metacwrap import MetaCWrap
+from .prototype import Prototype
+from .basecclass import BaseCClass
+from .basecenum import BaseCEnum
+from .basecvalue import BaseCValue
diff --git a/python/cwrap/basecclass.py b/python/cwrap/basecclass.py
new file mode 100644
index 0000000..e2f9549
--- /dev/null
+++ b/python/cwrap/basecclass.py
@@ -0,0 +1,100 @@
+import ctypes
+from cwrap import MetaCWrap
+
+
+class BaseCClass(object):
+ __metaclass__ = MetaCWrap
+
+ def __init__(self, c_pointer, parent=None, is_reference=False):
+ if c_pointer == 0 or c_pointer is None:
+ raise ValueError("Must have a valid (not null) pointer value!")
+
+ if c_pointer < 0:
+ raise ValueError("The pointer value is negative! This may be correct, but usually is not!")
+
+ self.__c_pointer = c_pointer
+ self.__parent = parent
+ self.__is_reference = is_reference
+
+ def __new__(cls, *more, **kwargs):
+ obj = super(BaseCClass, cls).__new__(cls)
+ obj.__c_pointer = None
+ obj.__parent = None
+ obj.__is_reference = False
+
+ return obj
+
+ @classmethod
+ def from_param(cls, c_class_object):
+ if c_class_object is not None and not isinstance(c_class_object, BaseCClass):
+ raise ValueError("c_class_object must be a BaseCClass instance!")
+
+ if c_class_object is None:
+ return ctypes.c_void_p()
+ else:
+ return ctypes.c_void_p(c_class_object.__c_pointer)
+
+ @classmethod
+ def createPythonObject(cls, c_pointer):
+ if c_pointer is not None:
+ new_obj = cls.__new__(cls)
+ BaseCClass.__init__(new_obj, c_pointer=c_pointer, parent=None, is_reference=False)
+ return new_obj
+ else:
+ return None
+
+ @classmethod
+ def createCReference(cls, c_pointer, parent=None):
+ if c_pointer is not None:
+ new_obj = cls.__new__(cls)
+ BaseCClass.__init__(new_obj, c_pointer=c_pointer, parent=parent, is_reference=True)
+ return new_obj
+ else:
+ return None
+
+ @classmethod
+ def storageType(cls):
+ return ctypes.c_void_p
+
+ def convertToCReference(self, parent):
+ self.__is_reference = True
+ self.__parent = parent
+
+
+ def setParent(self, parent=None):
+ if self.__is_reference:
+ self.__parent = parent
+ else:
+ raise UserWarning("Can only set parent on reference types!")
+
+ return self
+
+ def isReference(self):
+ """ @rtype: bool """
+ return self.__is_reference
+
+ def parent(self):
+ return self.__parent
+
+ def __eq__(self, other):
+ # This is the last resort comparison function; it will do a
+ # plain pointer comparison on the underlying C object; or
+ # Python is-same-object comparison.
+ if isinstance(other, BaseCClass):
+ return self.__c_pointer == other.__c_pointer
+ else:
+ return super(BaseCClass , self).__eq__(other)
+
+
+ def free(self):
+ raise NotImplementedError("A BaseCClass requires a free method implementation!")
+
+
+
+ def __del__(self):
+ if self.free is not None:
+ if not self.__is_reference:
+ # Important to check the c_pointer; in the case of failed object creation
+ # we can have a Python object with c_pointer == None.
+ if self.__c_pointer > 0:
+ self.free()
diff --git a/python/cwrap/basecenum.py b/python/cwrap/basecenum.py
new file mode 100644
index 0000000..29178a4
--- /dev/null
+++ b/python/cwrap/basecenum.py
@@ -0,0 +1,119 @@
+from cwrap import MetaCWrap, Prototype
+
+class BaseCEnum(object):
+ __metaclass__ = MetaCWrap
+ enum_namespace = {}
+
+ def __init__(self, *args, **kwargs):
+ if not self in self.enum_namespace[self.__class__]:
+ raise NotImplementedError("Can not be instantiated directly!")
+
+ def __new__(cls, *args, **kwargs):
+ if len(args) == 1:
+ enum = cls.__resolveEnum(args[0])
+
+ if enum is None:
+ raise ValueError("Unknown enum value: %i" % args[0])
+
+ return enum
+ else:
+ obj = super(BaseCEnum, cls).__new__(cls, *args)
+ obj.name = None
+ obj.value = None
+ return obj
+
+ @classmethod
+ def from_param(cls, c_class_object):
+ if not isinstance(c_class_object, BaseCEnum):
+ raise ValueError("c_class_object must be an BaseCEnum instance!")
+ return c_class_object.value
+
+ @classmethod
+ def addEnum(cls, name, value):
+ if not isinstance(value, int):
+ raise ValueError("Value must be an integer!")
+
+ enum = cls.__new__(cls)
+ enum.name = name
+ enum.value = value
+
+ setattr(cls, name, enum)
+
+ if not cls.enum_namespace.has_key(cls):
+ cls.enum_namespace[cls] = []
+
+ cls.enum_namespace[cls].append(enum)
+
+ @classmethod
+ def enums(cls):
+ return list(cls.enum_namespace[cls])
+
+ def __eq__(self, other):
+ if isinstance(other, self.__class__):
+ return self.value == other.value
+
+ if isinstance(other, int):
+ return self.value == other
+
+ return False
+
+ def __str__(self):
+ return self.name
+
+ def __add__(self, other):
+ self.__assertOtherIsSameType(other)
+ value = self.value + other.value
+ return self.__resolveOrCreateEnum(value)
+
+ def __or__(self, other):
+ self.__assertOtherIsSameType(other)
+ value = self.value | other.value
+ return self.__resolveOrCreateEnum(value)
+
+
+ def __xor__(self, other):
+ self.__assertOtherIsSameType(other)
+ value = self.value ^ other.value
+ return self.__resolveOrCreateEnum(value)
+
+ def __and__(self, other):
+ self.__assertOtherIsSameType(other)
+ value = self.value & other.value
+ return self.__resolveOrCreateEnum(value)
+
+ def __int__(self):
+ return self.value
+
+ def __contains__(self, item):
+ return self & item == item
+
+ @classmethod
+ def __createEnum(cls, value):
+ enum = cls.__new__(cls)
+ enum.name = "Unnamed '%s' enum with value: %i" % (str(cls.__name__), value)
+ enum.value = value
+ return enum
+
+ @classmethod
+ def __resolveOrCreateEnum(cls, value):
+ enum = cls.__resolveEnum(value)
+
+ if enum is not None:
+ return enum
+
+ return cls.__createEnum(value)
+
+ @classmethod
+ def __resolveEnum(cls, value):
+ for enum in cls.enum_namespace[cls]:
+ if enum.value == value:
+ return enum
+ return None
+
+ def __assertOtherIsSameType(self, other):
+ assert isinstance(other, self.__class__), "Can only operate on enums of same type: %s =! %s" % (
+ self.__class__.__name__, other.__class__.__name__)
+
+ @classmethod
+ def registerEnum(cls, library, enum_name):
+ Prototype.registerType(enum_name, cls)
diff --git a/python/cwrap/basecvalue.py b/python/cwrap/basecvalue.py
new file mode 100644
index 0000000..e885ac0
--- /dev/null
+++ b/python/cwrap/basecvalue.py
@@ -0,0 +1,47 @@
+from ctypes import pointer, c_long, c_int, c_bool, c_float, c_double, c_byte, c_short, c_char, c_ubyte, c_ushort, c_uint, c_ulong, c_size_t
+
+from cwrap import MetaCWrap
+
+
+class BaseCValue(object):
+ __metaclass__ = MetaCWrap
+ DATA_TYPE = None
+ LEGAL_TYPES = [c_byte, c_ubyte, c_short, c_ushort, c_int, c_uint, c_long, c_ulong, c_bool, c_char, c_float, c_double, c_size_t]
+
+ def __init__(self, value):
+ super(BaseCValue, self).__init__()
+
+ if not self.DATA_TYPE in self.LEGAL_TYPES:
+ raise ValueError("DATA_TYPE must be one of these CTypes classes: %s" % BaseCValue.LEGAL_TYPES)
+
+ self.__value = self.cast(value)
+
+
+ def value(self):
+ return self.__value.value
+
+ @classmethod
+ def storageType(cls):
+ return cls.type()
+
+ @classmethod
+ def type(cls):
+ return cls.DATA_TYPE
+
+ @classmethod
+ def cast(cls, value):
+ return cls.DATA_TYPE(value)
+
+ def setValue(self, value):
+ self.__value = self.cast(value)
+
+ def asPointer(self):
+ return pointer(self.__value)
+
+ @classmethod
+ def from_param(cls, c_value_object):
+ if c_value_object is not None and not isinstance(c_value_object, BaseCValue):
+ raise ValueError("c_class_object must be a BaseCValue instance!")
+
+ return c_value_object.__value
+
diff --git a/python/cwrap/metacwrap.py b/python/cwrap/metacwrap.py
new file mode 100644
index 0000000..da31e66
--- /dev/null
+++ b/python/cwrap/metacwrap.py
@@ -0,0 +1,46 @@
+import re
+from types import MethodType
+
+from cwrap.prototype import registerType, Prototype
+
+
+def snakeCase(name):
+ s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name)
+ return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower()
+
+
+class MetaCWrap(type):
+ def __init__(cls, name, bases, attrs):
+ super(MetaCWrap, cls).__init__(name, bases, attrs)
+
+ is_return_type = False
+ storage_type = None
+
+ if "TYPE_NAME" in attrs:
+ type_name = attrs["TYPE_NAME"]
+ else:
+ type_name = snakeCase(name)
+
+ if hasattr(cls, "DATA_TYPE") or hasattr(cls, "enums"):
+ is_return_type = True
+
+ if hasattr(cls, "storageType"):
+ storage_type = cls.storageType()
+
+ registerType(type_name, cls, is_return_type=is_return_type, storage_type=storage_type)
+
+ if hasattr(cls, "createCReference"):
+ registerType("%s_ref" % type_name, cls.createCReference, is_return_type=True, storage_type=storage_type)
+
+ if hasattr(cls, "createPythonObject"):
+ registerType("%s_obj" % type_name, cls.createPythonObject, is_return_type=True, storage_type=storage_type)
+
+
+ for key, attr in attrs.items():
+ if isinstance(attr, Prototype):
+ attr.resolve()
+ attr.__name__ = key
+
+ if attr.shouldBeBound():
+ method = MethodType(attr, None, cls)
+ setattr(cls, key, method)
diff --git a/python/cwrap/prototype.py b/python/cwrap/prototype.py
new file mode 100644
index 0000000..34aaf82
--- /dev/null
+++ b/python/cwrap/prototype.py
@@ -0,0 +1,147 @@
+import ctypes
+import inspect
+import re
+
+import sys
+
+class TypeDefinition(object):
+ def __init__(self, type_class_or_function, is_return_type, storage_type):
+ self.storage_type = storage_type
+ self.is_return_type = is_return_type
+ self.type_class_or_function = type_class_or_function
+
+
+REGISTERED_TYPES = {}
+""":type: dict[str,TypeDefinition]"""
+
+
+def registerType(type_name, type_class_or_function, is_return_type=True, storage_type=None):
+ if type_name in REGISTERED_TYPES:
+ raise PrototypeError("Type: '%s' already registered!" % type_name)
+
+ REGISTERED_TYPES[type_name] = TypeDefinition(type_class_or_function, is_return_type, storage_type)
+
+ # print("Registered: %s for class: %s" % (type_name, repr(type_class_or_function)))
+
+registerType("void", None)
+registerType("void*", ctypes.c_void_p)
+registerType("uint", ctypes.c_uint)
+registerType("uint*", ctypes.POINTER(ctypes.c_uint))
+registerType("int", ctypes.c_int)
+registerType("int*", ctypes.POINTER(ctypes.c_int))
+registerType("int64", ctypes.c_int64)
+registerType("int64*", ctypes.POINTER(ctypes.c_int64))
+registerType("size_t", ctypes.c_size_t)
+registerType("size_t*", ctypes.POINTER(ctypes.c_size_t))
+registerType("bool", ctypes.c_bool)
+registerType("bool*", ctypes.POINTER(ctypes.c_bool))
+registerType("long", ctypes.c_long)
+registerType("long*", ctypes.POINTER(ctypes.c_long))
+registerType("char", ctypes.c_char)
+registerType("char*", ctypes.c_char_p)
+registerType("char**", ctypes.POINTER(ctypes.c_char_p))
+registerType("float", ctypes.c_float)
+registerType("float*", ctypes.POINTER(ctypes.c_float))
+registerType("double", ctypes.c_double)
+registerType("double*", ctypes.POINTER(ctypes.c_double))
+registerType("py_object", ctypes.py_object)
+
+PROTOTYPE_PATTERN = "(?P<return>[a-zA-Z][a-zA-Z0-9_*]*) +(?P<function>[a-zA-Z]\w*) *[(](?P<arguments>[a-zA-Z0-9_*, ]*)[)]"
+
+class PrototypeError(Exception):
+ pass
+
+
+class Prototype(object):
+ pattern = re.compile(PROTOTYPE_PATTERN)
+
+ def __init__(self, lib, prototype, bind=False):
+ super(Prototype, self).__init__()
+ self._lib = lib
+ self._prototype = prototype
+ self._bind = bind
+ self._func = None
+ self.__name__ = prototype
+ self._resolved = False
+
+
+ def _parseType(self, type_name):
+ """Convert a prototype definition type from string to a ctypes legal type."""
+ type_name = type_name.strip()
+
+ if type_name in REGISTERED_TYPES:
+ type_definition = REGISTERED_TYPES[type_name]
+ return type_definition.type_class_or_function, type_definition.storage_type
+ raise ValueError("Unknown type: %s" % type_name)
+
+
+ def shouldBeBound(self):
+ return self._bind
+
+ def resolve(self):
+ match = re.match(Prototype.pattern, self._prototype)
+ if not match:
+ raise PrototypeError("Illegal prototype definition: %s\n" % self._prototype)
+ else:
+ restype = match.groupdict()["return"]
+ function_name = match.groupdict()["function"]
+ self.__name__ = function_name
+ arguments = match.groupdict()["arguments"].split(",")
+
+ try:
+ func = getattr(self._lib, function_name)
+ except AttributeError:
+ raise PrototypeError("Can not find function: %s in library: %s" % (function_name , self._lib))
+
+ if not restype in REGISTERED_TYPES or not REGISTERED_TYPES[restype].is_return_type:
+ sys.stderr.write("The type used as return type: %s is not registered as a return type.\n" % restype)
+
+ return_type = self._parseType(restype)
+
+ if inspect.isclass(return_type):
+ sys.stderr.write(" Correct type may be: %s_ref or %s_obj.\n" % (restype, restype))
+
+ return None
+
+ return_type, storage_type = self._parseType(restype)
+
+ func.restype = return_type
+
+ if storage_type is not None:
+ func.restype = storage_type
+
+ def returnFunction(result, func, arguments):
+ return return_type(result)
+
+ func.errcheck = returnFunction
+
+ if len(arguments) == 1 and arguments[0].strip() == "":
+ func.argtypes = []
+ else:
+ argtypes = [self._parseType(arg)[0] for arg in arguments]
+ if len(argtypes) == 1 and argtypes[0] is None:
+ argtypes = []
+ func.argtypes = argtypes
+
+ self._func = func
+
+
+ def __call__(self, *args):
+ if not self._resolved:
+ self.resolve()
+ self._resolved = True
+
+ if self._func is None:
+ raise PrototypeError("Prototype has not been properly resolved!")
+ return self._func(*args)
+
+ def __repr__(self):
+ bound = ""
+ if self.shouldBeBound():
+ bound = ", bind=True"
+
+ return 'Prototype("%s"%s)' % (self._prototype, bound)
+
+ @classmethod
+ def registerType(cls, type_name, type_class_or_function, is_return_type=True, storage_type=None):
+ registerType(type_name, type_class_or_function, is_return_type=is_return_type, storage_type=storage_type)
diff --git a/python/segyio/CMakeLists.txt b/python/segyio/CMakeLists.txt
new file mode 100644
index 0000000..7345f1a
--- /dev/null
+++ b/python/segyio/CMakeLists.txt
@@ -0,0 +1,11 @@
+set(PYTHON_SOURCES
+ __init__.py
+ segy.py
+ tracefield.py
+ binfield.py
+ open.py
+ create.py
+ segysampleformat.py
+ tracesortingformat.py)
+
+add_python_package(segyio segyio "${PYTHON_SOURCES}")
diff --git a/python/segyio/__init__.py b/python/segyio/__init__.py
new file mode 100644
index 0000000..b2996f0
--- /dev/null
+++ b/python/segyio/__init__.py
@@ -0,0 +1,42 @@
+"""
+simple segy input/output
+
+Welcome to segyio. For help, examples and reference, type `help(function)` in
+your favourite python interpreter, or `pydoc function` in the unix console.
+
+The segy library attempts to be easy to use efficently for prototyping and
+interaction with possibly large segy files. File reading and writing is
+streaming, with large file support out of the box and without hassle. For a
+quick start on reading files, type `help(segyio.open)`.
+
+An open segy file is interacted with in modes, found in the segy module. For a
+reference with examples, please type `help(segyio.segy)`. For documentation on
+individual modes, please refer to the individual modes with
+`help(segyio.segy.file.[mode])`, or look it up in the aggregated segyio.segy.
+The available modes are:
+ * text, for textual headers including extended headers
+ * bin, for the binary header
+ * header, for the trace headers
+ * trace, for trace data
+ * iline, for inline biased operations
+ * xline, for crossline biased operations
+
+The primary data type is the numpy array. All examples use `np` for the numpy
+namespace. That means that any function that returns a trace, a set of samples
+or even full lines, returns a numpy array. This enables quick and easy
+mathematical operations on the data you care about.
+
+Segyio is designed to blend into regular python code, so python concepts that
+map to segy operations are written to behave similarly. That means that
+sequences of data support list lookup, slicing (`f.trace[0:10:2]`), `for x in`
+etc. Please refer to the individual mode's documentation for a more exhaustive
+list with examples.
+"""
+
+from .segysampleformat import SegySampleFormat
+from .tracesortingformat import TraceSortingFormat
+from .tracefield import TraceField
+from .binfield import BinField
+from .open import open
+from .create import create
+from .segy import file, spec
diff --git a/python/segyio/binfield.py b/python/segyio/binfield.py
new file mode 100644
index 0000000..f1dd91e
--- /dev/null
+++ b/python/segyio/binfield.py
@@ -0,0 +1,70 @@
+from cwrap import BaseCEnum
+
+class BinField(BaseCEnum):
+ TYPE_NAME = "SEGY_BINFIELD"
+
+ JobID = None
+ LineNumber = None
+ ReelNumber = None
+ Traces = None
+ AuxTraces = None
+ Interval = None
+ IntervalOriginal = None
+ Samples = None
+ SamplesOriginal = None
+ Format = None
+ EnsembleFold = None
+ SortingCode = None
+ VerticalSum = None
+ SweepFrequencyStart = None
+ SweepFrequencyEnd = None
+ SweepLength = None
+ Sweep = None
+ SweepChannel = None
+ SweepTaperStart = None
+ SweepTaperEnd = None
+ Taper = None
+ CorrelatedTraces = None
+ BinaryGainRecovery = None
+ AmplitudeRecovery = None
+ MeasurementSystem = None
+ ImpulseSignalPolarity = None
+ VibratoryPolarity = None
+ Unassigned1 = None
+ SEGYRevision = None
+ TraceFlag = None
+ ExtendedHeaders = None
+ Unassigned2 = None
+
+BinField.addEnum("JobID", 3201)
+BinField.addEnum("LineNumber", 3205)
+BinField.addEnum("ReelNumber", 3209)
+BinField.addEnum("Traces", 3213)
+BinField.addEnum("AuxTraces", 3215)
+BinField.addEnum("Interval", 3217)
+BinField.addEnum("IntervalOriginal", 3219)
+BinField.addEnum("Samples", 3221)
+BinField.addEnum("SamplesOriginal", 3223)
+BinField.addEnum("Format", 3225)
+BinField.addEnum("EnsembleFold", 3227)
+BinField.addEnum("SortingCode", 3229)
+BinField.addEnum("VerticalSum", 3231)
+BinField.addEnum("SweepFrequencyStart", 3233)
+BinField.addEnum("SweepFrequencyEnd", 3235)
+BinField.addEnum("SweepLength", 3237)
+BinField.addEnum("Sweep", 3239)
+BinField.addEnum("SweepChannel", 3241)
+BinField.addEnum("SweepTaperStart", 3243)
+BinField.addEnum("SweepTaperEnd", 3245)
+BinField.addEnum("Taper", 3247)
+BinField.addEnum("CorrelatedTraces", 3249)
+BinField.addEnum("BinaryGainRecovery", 3251)
+BinField.addEnum("AmplitudeRecovery", 3253)
+BinField.addEnum("MeasurementSystem", 3255)
+BinField.addEnum("ImpulseSignalPolarity", 3257)
+BinField.addEnum("VibratoryPolarity", 3259)
+BinField.addEnum("Unassigned1", 3261)
+BinField.addEnum("SEGYRevision", 3501)
+BinField.addEnum("TraceFlag", 3503)
+BinField.addEnum("ExtendedHeaders", 3505)
+BinField.addEnum("Unassigned2", 3507)
diff --git a/python/segyio/create.py b/python/segyio/create.py
new file mode 100644
index 0000000..d0c64d0
--- /dev/null
+++ b/python/segyio/create.py
@@ -0,0 +1,135 @@
+import datetime
+import ctypes as ct
+import segyio
+
+def default_text_header(iline, xline, offset):
+ return ''.join([
+ "C 1 DATE: %s ",
+ "C 2 AN INCREASE IN AMPLITUDE EQUALS AN INCREASE IN ACOUSTIC IMPEDANCE ",
+ "C 3 Written by libsegyio (python) ",
+ "C 4 ",
+ "C 5 ",
+ "C 6 ",
+ "C 7 ",
+ "C 8 ",
+ "C 9 ",
+ "C10 ",
+ "C11 TRACE HEADER POSITION: ",
+ "C12 INLINE BYTES %03d-%03d | OFFSET BYTES %03d-%03d ",
+ "C13 CROSSLINE BYTES %03d-%03d | ",
+ "C14 ",
+ "C15 END EBCDIC HEADER ",
+ "C16 ",
+ "C17 ",
+ "C18 ",
+ "C19 ",
+ "C20 ",
+ "C21 ",
+ "C22 ",
+ "C23 ",
+ "C24 ",
+ "C25 ",
+ "C26 ",
+ "C27 ",
+ "C28 ",
+ "C29 ",
+ "C30 ",
+ "C31 ",
+ "C32 ",
+ "C33 ",
+ "C34 ",
+ "C35 ",
+ "C36 ",
+ "C37 ",
+ "C38 ",
+ "C39 ",
+ "C40 \x80"]) \
+ % (datetime.date.today(), iline, iline + 4, int(offset), int(offset) + 4, xline, xline + 4)
+
+def create(filename, spec):
+ """Create a new segy file.
+
+ Create a new segy file with the geometry and properties given by `spec`.
+ This enables creating SEGY files from your data. The created file supports
+ all segyio modes, but has an emphasis on writing. The spec must be
+ complete, otherwise an exception will be raised. A default, empty spec can
+ be created with `segyio.spec()`.
+
+ Very little data is written to the file, so just calling create is not
+ sufficient to re-read the file with segyio. Rather, every trace header and
+ trace must be written to for the file to be considered complete.
+
+ Create should be used together with python's `with` statement. This ensure
+ the data is written. Please refer to the examples.
+
+ Args:
+ filename (str): Path to file to open.
+ spec (:obj: `spec`): Structure of the segy file.
+
+ Examples:
+ Create a file::
+ >>> spec = segyio.spec()
+ >>> spec.ilines = [1, 2, 3, 4]
+ >>> spec.xlines = [11, 12, 13]
+ >>> spec.samples = 50
+ >>> spec.sorting = 2
+ >>> spec.format = 1
+ >>> with segyio.create(path, spec) as f:
+ ... ## fill the file with data
+ ...
+
+ Copy a file, but shorten all traces by 50 samples::
+ >>> with segyio.open(srcpath) as src:
+ ... spec = segyio.spec()
+ ... spec.sorting = src.sorting
+ ... spec.format = src.format
+ ... spec.samples = src.samples - 50
+ ... spec.ilines = src.ilines
+ ... spec.xline = src.xlines
+ ... with segyio.create(dstpath, spec) as dst:
+ ... dst.text[0] = src.text[0]
+ ... dst.bin = src.bin
+ ... dst.header = src.header
+ ... dst.trace = src.trace
+ """
+ f = segyio.file(filename, "w+")
+
+ f.samples = spec.samples
+ f.ext_headers = spec.ext_headers
+ f._bsz = segyio.file._trace_bsize(f.samples)
+ f._tr0 = -1 + segyio.file._textsize() + \
+ segyio.file._binheader_size() + \
+ (spec.ext_headers * segyio.file._textsize())
+ f.sorting = spec.sorting
+ f._fmt = spec.format
+ f.offsets = spec.offsets
+ f.tracecount = len(spec.ilines) * len(spec.xlines) * spec.offsets
+
+ f._il = int(spec.iline)
+ f.ilines = spec.ilines
+ f._raw_ilines = (ct.c_uint * len(f.ilines))()
+ for i, x in enumerate(f.ilines):
+ f._raw_ilines[i] = x
+
+ f._xl = int(spec.xline)
+ f.xlines = spec.xlines
+ f._raw_xlines = (ct.c_uint * len(f.xlines))()
+ for i, x in enumerate(f.xlines):
+ f._raw_xlines[i] = x
+
+
+ f._iline_length = f._init_iline_length(len(f.xlines))
+ f._iline_stride = f._init_iline_stride(len(f.ilines))
+
+ f._xline_length = f._init_xline_length(len(f.ilines))
+ f._xline_stride = f._init_xline_stride(len(f.xlines))
+
+ f.text[0] = default_text_header(f._il, f._xl, segyio.TraceField.offset)
+ f.bin = { 3213: f.tracecount,
+ 3217: 4000,
+ 3221: f.samples,
+ 3225: f.format,
+ 3505: f.ext_headers,
+ }
+
+ return f
diff --git a/python/segyio/open.py b/python/segyio/open.py
new file mode 100644
index 0000000..f9b1580
--- /dev/null
+++ b/python/segyio/open.py
@@ -0,0 +1,74 @@
+import segyio
+
+def open(filename, mode = "r", iline = 189, xline = 193):
+ """Open a segy file.
+
+ Opens a segy file and tries to figure out its sorting, inline numbers,
+ crossline numbers, and offsets, and enables reading and writing to this
+ file in a simple manner.
+
+ For reading, the access mode "r" is preferred. All write operations will
+ raise an exception. For writing, the mode "r+" is preferred (as "rw" would
+ truncate the file). The modes used are standard C file modes; please refer
+ to that documentation for a complete reference.
+
+ Open should be used together with python's `with` statement. Please refer
+ to the examples. When the `with` statement is used the file will
+ automatically be closed when the routine completes or an exception is
+ raised.
+
+ Args:
+ filename (str): Path to file to open.
+ mode (str, optional): File access mode, defaults to "r".
+ iline (TraceField): Inline number field in the trace headers. Defaults
+ to 189 as per the SEGY specification.
+ xline (TraceField): Crossline number field in the trace headers.
+ Defaults to 193 as per the SEGY specification.
+
+ Examples:
+ Open a file in read-only mode::
+ >>> with segyio.open(path, "r") as f:
+ ... print(f.ilines)
+ ...
+
+ Open a file in read-write mode::
+ >>> with segyio.open(path, "r+") as f:
+ ... f.trace = np.arange(100)
+ ...
+
+ Open two files at once::
+ >>> with segyio.open(path) as src, segyio.open(path, "r+") as dst:
+ ... dst.trace = src.trace # copy all traces from src to dst
+ ...
+ """
+ f = segyio.file(filename, mode, iline, xline)
+
+ try:
+ header = f.bin.buf
+
+ f.samples = f._get_samples(header)
+ f._tr0 = f._trace0(header)
+ f._fmt = f._format(header)
+ f._bsz = f._trace_bsize(f.samples)
+ f.ext_headers = (f._tr0 - 3600) / 3200 # should probably be from C
+
+ f.tracecount = f._init_traces()
+ f.sorting = f._init_sorting()
+ f.offsets = f._init_offsets()
+
+ iline_count, xline_count = f._init_line_count()
+
+ f.ilines, f._raw_ilines = f._init_ilines(iline_count, xline_count)
+ f._iline_length = f._init_iline_length(xline_count)
+ f._iline_stride = f._init_iline_stride(iline_count)
+
+ f.xlines, f._raw_xlines = f._init_xlines(iline_count, xline_count)
+ f._xline_length = f._init_xline_length(iline_count)
+ f._xline_stride = f._init_xline_stride(xline_count)
+
+ except:
+ f.close()
+ raise
+
+ return f
+
diff --git a/python/segyio/segy.py b/python/segyio/segy.py
new file mode 100644
index 0000000..1ef6c59
--- /dev/null
+++ b/python/segyio/segy.py
@@ -0,0 +1,1302 @@
+"""
+segy modes
+
+Welcome to segyio.segy. Here you will find references and examples for the
+various segy modes and how to interact with segy files. To start interacting
+with files, please refer to the segyio.open and segyio.create documentation, by
+typing `help(segyio.open)` or `help(segyio.create)`.
+
+The primary way of obtaining a file instance is calling segyio.open. When you
+have a file instance you can interact with it as described in this module.
+
+The explanations and examples here are meant as a quick guide and reference.
+You can also have a look at the example programs that are distributed with
+segyio which you can find in the examples directory or where your distribution
+installs example programs.
+"""
+
+import sys, os, errno
+import itertools
+import numpy as np
+import ctypes as ct
+from cwrap import BaseCClass, BaseCEnum, Prototype
+from segyio import TraceField, BinField
+
+class _Segyio(Prototype):
+ SEGYIO_LIB = ct.CDLL("libsegyio.so", use_errno=True)
+
+ def __init__(self, prototype, bind=True):
+ super(_Segyio, self).__init__(_Segyio.SEGYIO_LIB, prototype, bind=bind)
+
+def _floatp(obj):
+ return obj.ctypes.data_as(ct.POINTER(ct.c_float))
+
+# match the SEGY_ERROR enum from segy.h
+class _error(BaseCEnum):
+ TYPE_NAME = "SEGY_ERROR"
+
+ OK = None
+ FOPEN_ERROR = None
+ FSEEK_ERROR = None
+ FREAD_ERROR = None
+ FWRITE_ERROR = None
+ INVALID_FIELD = None
+ INVALID_SORTING = None
+ MISSING_LINE_INDEX = None
+ INVALID_OFFSETS = None
+ TRACE_SIZE_MISMATCH = None
+
+_error.addEnum("OK", 0)
+_error.addEnum("FOPEN_ERROR", 1)
+_error.addEnum("FSEEK_ERROR", 2)
+_error.addEnum("FREAD_ERROR", 3)
+_error.addEnum("FWRITE_ERROR", 4)
+_error.addEnum("INVALID_FIELD", 5)
+_error.addEnum("INVALID_SORTING", 6)
+_error.addEnum("MISSING_LINE_INDEX", 7)
+_error.addEnum("INVALID_OFFSETS", 8)
+_error.addEnum("TRACE_SIZE_MISMATCH", 9)
+
+def _header_buffer(buf = None):
+ if buf is None:
+ return (ct.c_char * 240)()
+
+ if len(buf) < 240:
+ raise ValueError("Buffer must be a minimum %d size'd byte array." % 240)
+
+ return buf
+
+def _set_field(buf, field, x):
+ errc = file._set_field(buf, int(field), x)
+ err = _error(errc)
+
+ if err != _error.OK:
+ raise IndexError("Invalid byte offset %d" % field)
+
+def _get_field(buf, field, x):
+ errc = file._get_field(buf, int(field), ct.byref(x))
+ err = _error(errc)
+
+ if err != _error.OK:
+ raise IndexError("Invalid byte offset %d" % field)
+
+ return int(x.value)
+
+def _write_header(buf, segy, traceno):
+ errc = segy._write_header(traceno, buf, segy._tr0, segy._bsz)
+ err = _error(errc)
+
+ if err != _error.OK:
+ errno = ct.get_errno()
+ raise OSError(errno, "Error writing header for trace %d: %s" % (traceno, os.strerror(errno)))
+
+
+class _line:
+ """ Line mode for traces and trace headers. Internal.
+
+ The _line class provides an interface for line-oriented operations. The
+ line reading operations themselves are not streaming - it's assumed than
+ when a line is queried it's somewhat limited in size and will comfortably
+ fit in memory, and that the full line is interesting. This also applies to
+ line headers; however, all returned values support the iterable protocol so
+ they work fine together with the streaming bits of this library.
+
+ _line should not be instantiated directly by users, but rather returned
+ from the iline/xline properties of file or from the header mode. Any
+ direct construction of this should be conisdered an error.
+ """
+ def __init__(self, segy, length, stride, lines, raw_lines, other_lines, buffn, readfn, writefn, name):
+ self.segy = segy
+ self.len = length
+ self.stride = stride
+ self.lines = lines
+ self.raw_lines = raw_lines
+ self.other_lines = other_lines
+ self.name = name
+ self.buffn = buffn
+ self.readfn = readfn
+ self.writefn = writefn
+
+ def __getitem__(self, lineno, buf = None):
+ if isinstance(lineno, tuple):
+ return self.__getitem__(lineno[0], lineno[1])
+
+ buf = self.buffn(buf)
+
+ if isinstance(lineno, int):
+ t0 = self.segy._fread_trace0(lineno, len(self.other_lines), self.stride, self.raw_lines, self.name)
+ return self.readfn(t0, self.len, self.stride, buf)
+
+ elif isinstance(lineno, slice):
+ # in order to support [:end] syntax, we must make sure
+ # start has a non-None value. lineno.indices() would set it
+ # to 0, but we don't know if that's a reasonable value or
+ # not. If start is None we set it to the first line
+ if lineno.start is None:
+ lineno = slice(self.lines[0], lineno.stop, lineno.step)
+
+ def gen():
+ s = set(self.lines)
+ rng = xrange(*lineno.indices(self.lines[-1] + 1))
+
+ # use __getitem__ lookup to avoid tuple
+ # construction and unpacking and fast-forward
+ # into the interesting code path
+ for i in itertools.ifilter(s.__contains__, rng):
+ yield self.__getitem__(i, buf)
+
+ return gen()
+
+ def __setitem__(self, lineno, val):
+ if isinstance(lineno, slice):
+ if lineno.start is None:
+ lineno = slice(self.lines[0], lineno.stop, lineno.step)
+
+ rng = xrange(*lineno.indices(self.lines[-1] + 1))
+ s = set(self.lines)
+
+ for i, x in itertools.izip(filter(s.__contains__, rng), val):
+ self.__setitem__(i, x)
+
+ return
+
+ t0 = self.segy._fread_trace0(lineno, len(self.other_lines), self.stride, self.raw_lines, self.name)
+ self.writefn(t0, self.len, self.stride, val)
+
+ def __len__(self):
+ return len(self.lines)
+
+ def __iter__(self):
+ buf = self.buffn()
+ for i in self.lines:
+ yield self.__getitem__(i, buf)
+
+class _header:
+ def __init__(self, segy):
+ self.segy = segy
+
+ class proxy:
+ def __init__(inner, buf, traceno = None, segy = None, get_field = _get_field, set_field = _set_field, write = _write_header, field_type = TraceField):
+ inner.buf = buf
+ inner.traceno = traceno
+ inner._segy = segy
+ inner._get_field = get_field
+ inner._set_field = set_field
+ inner._field_type = field_type
+ inner._write = write
+
+ def __getitem__(inner, field):
+ val = ct.c_int()
+
+ # add some structure so we can always iterate over fields
+ if isinstance(field, int) or isinstance(field, inner._field_type):
+ field = [field]
+
+ d = { inner._field_type(f): inner._get_field(inner.buf, f, val) for f in field }
+
+ # unpack the dictionary. if header[field] is requested, a
+ # plain, unstructed output is expected, but header[f1,f2,f3]
+ # yields a dict
+ if len(d) == 1:
+ return d.values()[0]
+
+ return d
+
+ def __setitem__(inner, field, val):
+ inner._set_field(inner.buf, field, val)
+ inner._write(inner.buf, inner._segy, inner.traceno)
+
+ def __getitem__(self, traceno, buf = None):
+ if isinstance(traceno, tuple):
+ return self.__getitem__(traceno[0], traceno[1])
+
+ buf = _header_buffer(buf)
+
+ if isinstance(traceno, slice):
+ def gen():
+ for i in xrange(*traceno.indices(self.segy.tracecount)):
+ yield self.__getitem__(i, buf)
+
+ return gen()
+
+ buf = self.segy._readh(traceno, buf)
+ return _header.proxy(buf, traceno = traceno, segy = self.segy)
+
+ def __setitem__(self, traceno, val):
+ buf = None
+
+ # library-provided loops can re-use a buffer for the lookup, even in
+ # __setitem__, so we might need to unpack the tuple to reuse the buffer
+ if isinstance(traceno, tuple):
+ buf = traceno[1]
+ traceno = traceno[0]
+
+ if isinstance(val, dict):
+ try:
+ # try to read a buffer. If the file was created by
+ # libsegyio this might not have been written to before and
+ # getitem might fail, so we try to read it and if it fails
+ # we check if we're still within bounds if we are we create
+ # an empty header and write to that
+ buf = self.__getitem__(traceno, buf).buf
+
+ except:
+ if traceno >= self.segy.tracecount: raise
+ buf = _header_buffer(buf)
+
+ for f, v in val.items():
+ _set_field(buf, f, v)
+
+ else:
+ buf = val.buf
+
+ _write_header(buf, self.segy, traceno)
+
+ def __iter__(self):
+ return self[:]
+
+ def readfn(self, t0, length, stride, buf):
+ def gen():
+ start = t0
+ stop = t0 + (length * stride)
+ for i in xrange(start, stop, stride):
+ self.segy._readh(i, buf)
+ yield _header.proxy(buf, traceno = i, segy = self.segy)
+
+ return gen()
+
+ def writefn(self, t0, length, stride, val):
+ start = t0
+ stop = t0 + (length * stride)
+
+ if isinstance(val, _header.proxy) or isinstance(val, dict):
+ val = itertools.repeat(val)
+
+ for i, x in itertools.izip(xrange(start, stop, stride), val):
+ self[i] = x
+
+
+ @property
+ def iline(self):
+ segy = self.segy
+ length = segy._iline_length
+ stride = segy._iline_stride
+ lines = segy.ilines
+ raw_lines = segy._raw_ilines
+ other_lines = segy.xlines
+ buffn = _header_buffer
+
+ return _line(segy, length, stride, lines, raw_lines, other_lines, buffn, self.readfn, self.writefn, "Inline")
+
+ @property
+ def xline(self):
+ segy = self.segy
+ length = segy._xline_length
+ stride = segy._xline_stride
+ lines = segy.xlines
+ raw_lines = segy._raw_xlines
+ other_lines = segy.xlines
+ buffn = _header_buffer
+
+ return _line(segy, length, stride, lines, raw_lines, other_lines, buffn, self.readfn, self.writefn, "Crossline")
+
+ def __setattr__(self, name, value):
+ """Write iterables to lines
+
+ Examples:
+ setattr supports writing to *all* inlines and crosslines via
+ assignment, regardless of data source and format. Will respect the
+ sample size and structure of the file being assigned to, so if the
+ argument traces are longer than that of the file being written to
+ the surplus data will be ignored. Uses same rules for writing as
+ `f.iline[i] = x`.
+ """
+ if name == "iline":
+ for i, src in itertools.izip(self.segy.ilines, value):
+ self.iline[i] = src
+
+ if name == "xline":
+ for i, src in itertools.izip(self.segy.xlines, value):
+ self.xline[i] = src
+
+ else:
+ self.__dict__[name] = value
+ return
+
+class file(BaseCClass):
+ TYPE_NAME = "FILE"
+
+ _open = _Segyio("void* fopen(char*, char*)", bind = False)
+ _flush = _Segyio("int fflush(FILE)")
+ _close = _Segyio("int fclose(FILE)")
+
+ _binheader_size = _Segyio("uint segy_binheader_size()", bind = False)
+ _binheader = _Segyio("int segy_binheader(FILE, char*)")
+ _write_binheader = _Segyio("int segy_write_binheader(FILE, char*)")
+
+ _trace0 = _Segyio("int segy_trace0(char*)", bind = False)
+ _get_samples = _Segyio("uint segy_samples(char*)", bind = False)
+ _format = _Segyio("int segy_format(char*)", bind = False)
+ _sorting = _Segyio("int segy_sorting(FILE, int, int, int*, long, uint)")
+ _trace_bsize = _Segyio("uint segy_trace_bsize(uint)", bind = False)
+ _traces = _Segyio("uint segy_traces(FILE, uint*, long, uint)")
+ _offsets = _Segyio("uint segy_offsets(FILE, int, int, uint, uint*, long, uint)")
+
+ _get_field = _Segyio("int segy_get_field(char*, int, int*)", bind = False)
+ _set_field = _Segyio("int segy_set_field(char*, int, int)", bind = False)
+
+ _get_bfield = _Segyio("int segy_get_bfield(char*, int, int*)", bind = False)
+ _set_bfield = _Segyio("int segy_set_bfield(char*, int, int)", bind = False)
+
+ _to_native = _Segyio("int segy_to_native(int, uint, float*)", bind = False)
+ _from_native = _Segyio("int segy_from_native(int, uint, float*)", bind = False)
+
+ _read_header = _Segyio("int segy_traceheader(FILE, uint, char*, long, uint)")
+ _write_header = _Segyio("int segy_write_traceheader(FILE, uint, char*, long, uint)")
+ _read_trace = _Segyio("int segy_readtrace(FILE, uint, float*, long, uint)")
+ _write_trace = _Segyio("int segy_writetrace(FILE, uint, float*, long, uint)")
+
+ _count_lines = _Segyio("int segy_count_lines(FILE, int, uint, uint*, uint*, long, uint)")
+ _inline_length = _Segyio("int segy_inline_length(int, uint, uint, uint, uint*)", bind = False)
+ _inline_stride = _Segyio("int segy_inline_stride(int, uint, uint*)", bind = False)
+ _inline_indices = _Segyio("int segy_inline_indices(FILE, int, int, uint, uint, uint, uint*, long, uint)")
+ _crossline_length = _Segyio("int segy_crossline_length(int, uint, uint, uint, uint*)", bind = False)
+ _crossline_stride = _Segyio("int segy_crossline_stride(int, uint, uint*)", bind = False)
+ _crossline_indices = _Segyio("int segy_crossline_indices(FILE, int, int, uint, uint, uint, uint*, long, uint)")
+ _line_trace0 = _Segyio("int segy_line_trace0( uint, uint, uint, uint*, uint, uint*)", bind = False)
+ _read_line = _Segyio("int segy_read_line(FILE, uint, uint, uint, float*, long, uint)")
+ _write_line = _Segyio("int segy_write_line(FILE, uint, uint, uint, float*, long, uint)")
+
+ _textsize = _Segyio("int segy_textheader_size()", bind = False)
+ _texthdr = _Segyio("int segy_textheader(FILE, char*)")
+ _write_texthdr = _Segyio("int segy_write_textheader(FILE, uint, char*)")
+
+ def __init__(self, filename, mode, iline = 189 , xline = 193, t0 = 1111.0 ):
+ """
+ Constructor, internal.
+ """
+ self._filename = filename
+ self._mode = mode
+ self._il = iline
+ self._xl = xline
+ fd = self._open(filename, mode)
+
+ if not fd:
+ errno = ct.get_errno()
+ strerror = os.strerror(errno)
+ raise OSError(errno, "Opening file '%s' failed: %s" % (filename, strerror))
+
+ super(file, self).__init__(fd)
+
+ def _init_traces(self):
+ traces = ct.c_uint()
+ errc = self._traces(ct.byref(traces), self._tr0, self._bsz)
+ err = _error(errc)
+
+ if err == _error.OK:
+ return int(traces.value)
+
+ if err == _error.TRACE_SIZE_MISMATCH:
+ raise RuntimeError("Number of traces is not consistent with file size. File probably corrupt.")
+
+ errno = ct.get_errno()
+ raise OSError("Error while detecting number of traces: %s" % os.strerror(errno))
+
+ def _init_sorting(self):
+ sorting = ct.c_int()
+ errc = self._sorting( self._il, self._xl, ct.byref(sorting), self._tr0, self._bsz)
+
+ err = _error(errc)
+
+ if err == _error.OK:
+ return int(sorting.value)
+
+ if err == _error.INVALID_FIELD:
+ raise ValueError("Invalid inline (%d) or crossline (%d) field/byte offset. "\
+ "Too large or between valid byte offsets" % (self._il, self._xl))
+
+ if err == _error.INVALID_SORTING:
+ raise RuntimeError("Unable to determine sorting. File probably corrupt.")
+
+ errno = ct.get_errno()
+ raise OSError(errno, "Error while detecting file sorting: %s" % os.strerror(errno))
+
+ return int(sorting.value)
+
+ def _init_offsets(self):
+ offsets = ct.c_uint()
+ errc = self._offsets(self._il, self._xl, self.tracecount, ct.byref(offsets), self._tr0, self._bsz)
+
+ err = _error(errc)
+
+ if err == _error.OK:
+ return int(offsets.value)
+
+ if err == _error.INVALID_FIELD:
+ raise ValueError("Invalid inline (%d) or crossline (%d) field/byte offset. "\
+ "Too large or between valid byte offsets" % (self._il, self._xl))
+
+ if err == _error.INVALID_OFFSETS:
+ raise RuntimeError("Found more offsets than traces. File probably corrupt.")
+
+ def _init_line_count(self):
+ ilines_sz, xlines_sz = ct.c_uint(), ct.c_uint()
+
+ if self.sorting == 1: #crossline sorted
+ fi = self._il
+ l1out = ct.byref(xlines_sz)
+ l2out = ct.byref(ilines_sz)
+ elif self.sorting == 2: #inline sorted
+ fi = self._xl
+ l1out = ct.byref(ilines_sz)
+ l2out = ct.byref(xlines_sz)
+ else:
+ raise RuntimeError("Unable to determine sorting. File probably corrupt.")
+
+ errc = self._count_lines(fi, self.offsets, l1out, l2out, self._tr0, self._bsz)
+ err = _error(errc)
+
+ if err == _error.OK:
+ return int(ilines_sz.value), int(xlines_sz.value)
+
+ errno = ct.get_errno()
+ raise OSError(errno, "Error while counting lines: %s", os.strerror(errno))
+
+
+ def _init_ilines(self, iline_count, xline_count):
+ ilines = (ct.c_uint * iline_count)()
+ errc = self._inline_indices(self._il, self.sorting, iline_count, xline_count, self.offsets, ilines, self._tr0, self._bsz)
+ err = _error(errc)
+
+ if err == _error.OK:
+ return map(int, ilines), ilines
+
+ if err == _error.INVALID_SORTING:
+ raise RuntimeError("Unknown file sorting.")
+
+ errno = ct.get_errno()
+ raise OSError(errno, "Error while reading inline indices: %s", os.strerror(errno))
+
+ def _init_iline_length(self, xline_count):
+ length = ct.c_uint()
+ errc = self._inline_length(self.sorting, self.tracecount, xline_count, self.offsets, ct.byref(length))
+ err = _error(errc)
+
+ if err == _error.OK:
+ return int(length.value)
+
+ if err == _error.INVALID_SORTING:
+ raise RuntimeError("Unknown file sorting.")
+
+ errno = ct.get_errno()
+ raise OSError(errno, "Error while determining inline length: %s", os.strerror(errno))
+
+ def _init_iline_stride(self, iline_count):
+ stride = ct.c_uint()
+ errc = self._inline_stride(self.sorting, iline_count, ct.byref(stride))
+ err = _error(errc)
+
+ if err == _error.OK:
+ return int(stride.value)
+
+ if err == _error.INVALID_SORTING:
+ raise RuntimeError("Unknown file sorting.")
+
+ def _init_xlines(self, iline_count, xline_count):
+ xlines = (ct.c_uint * xline_count)()
+ errc = self._crossline_indices(self._xl, self.sorting, iline_count, xline_count, self.offsets, xlines, self._tr0, self._bsz)
+ err = _error(errc)
+
+ if err == _error.OK:
+ return map(int, xlines), xlines
+
+ if err == _error.INVALID_SORTING:
+ raise RuntimeError("Unknown file sorting.")
+
+ errno = ct.get_errno()
+ raise OSError(errno, "Error while reading crossline indices: %s", os.strerror(errno))
+
+ def _init_xline_length(self, iline_count):
+ length = ct.c_uint()
+ errc = self._crossline_length(self.sorting, self.tracecount, iline_count, self.offsets, ct.byref(length))
+ err = _error(errc)
+
+ if err == _error.OK:
+ return int(length.value)
+
+ if err == _error.INVALID_SORTING:
+ raise RuntimeError("Unknown file sorting.")
+
+ errno = ct.get_errno()
+ raise OSError(errno, "Error while determining crossline length: %s", os.strerror(errno))
+
+
+ def _init_xline_stride(self, xline_count):
+ stride = ct.c_uint()
+ errc = self._crossline_stride(self.sorting, xline_count, ct.byref(stride))
+ err = _error(errc)
+
+ if err == _error.OK:
+ return int(stride.value)
+
+ if err == _error.INVALID_SORTING:
+ raise RuntimeError("Unknown file sorting.")
+
+ def __enter__(self):
+ """Internal."""
+ return self
+
+ def __exit__(self, type, value, traceback):
+ """Internal."""
+ self.close()
+
+ def flush(self):
+ """Flush a file - write the library buffers to disk.
+
+ This method is mostly useful for testing.
+
+ It is not necessary to call this method unless you want to observe your
+ changes while the file is still open. The file will automatically be
+ flushed for you if you use the `with` statement when your routine is
+ completed.
+
+ Examples:
+ Flush::
+ >>> with segyio.open(path) as f:
+ ... # write something to f
+ ... f.flush()
+ """
+ self._flush()
+
+ def close(self):
+ """Close the file.
+
+ This method is mostly useful for testing.
+
+ It is not necessary to call this method if you're using the `with`
+ statement, which will close the file for you.
+ """
+ self._close()
+
+ @property
+ def header(self):
+ """ Interact with segy in header mode.
+
+ This mode gives access to reading and writing functionality of headers,
+ both in individual (trace) mode and line mode. Individual headers are
+ accessed via generators and are not read from or written to disk until
+ the generator is realised and the header in question is used. Supports
+ python slicing (yields a generator), as well as direct lookup.
+
+ Examples:
+ Reading a field in a trace::
+ >>> f.header[10][TraceField.offset]
+
+ Writing a field in a trace::
+ >>> f.header[10][TraceField.offset] = 5
+
+ Copy a header from another header::
+ >>> f.header[28] = f.header[29]
+
+ Reading multiple fields in a trace. If raw, numerical offsets are
+ used they must align with the defined byte offsets by the SEGY
+ specification::
+ >>> f.header[10][TraceField.offset, TraceField.INLINE_3D]
+ >>> f.header[10][37, 189]
+
+ Write multiple fields in a trace::
+ >>> f.header[10] = { 37: 5, TraceField.INLINE_3D: 2484 }
+
+ Iterate over headers and gather line numbers::
+ >>> [h[TraceField.INLINE_3D] for h in f.header]
+ >>> [h[25, 189] for h in f.header]
+
+ Write field in all headers::
+ >>> for h in f.header:
+ ... h[37] = 1
+ ... h = { TraceField.offset: 1, 2484: 10 }
+ ...
+
+ Read a field in 10 first headers::
+ >>> [h[25] for h in f.header[0:10]]
+
+ Read a field in every other header::
+ >>> [h[37] for h in f.header[::2]]
+
+ Write a field in every other header::
+ >>> for h in f.header[::2]:
+ ... h = { TraceField.offset = 2 }
+ ...
+
+ Cache a header:
+ >>> h = f.header[12]
+ >>> x = foo()
+ >>> h[37] = x
+
+ A convenient way for operating on all headers of a file is to use the
+ default full-file range. It will write headers 0, 1, ..., n, but uses
+ the iteration specified by the right-hand side (i.e. can skip headers
+ etc).
+
+ If the right-hand-side headers are exhausted before all the destination
+ file headers the writing will stop, i.e. not all all headers in the
+ destination file will be written to.
+
+ Copy headers from file f to file g::
+ >>> f.header = g.header
+
+ Set offset field::
+ >>> f.header = { TraceField.offset: 5 }
+
+ Copy every 12th header from the file g to f's 0, 1, 2...::
+ >>> f.header = g.header[::12]
+ >>> f.header[0] == g.header[0]
+ True
+ >>> f.header[1] == g.header[12]
+ True
+ >>> f.header[2] == g.header[2]
+ False
+ >>> f.header[2] == g.header[24]
+ True
+ """
+ return _header(self)
+
+ @header.setter
+ def header(self, val):
+ if isinstance(val, _header.proxy) or isinstance(val, dict):
+ val = itertools.repeat(val)
+
+ h, buf = self.header, None
+ for i, v in itertools.izip(xrange(self.tracecount), val):
+ h[i, buf] = v
+
+ @property
+ def trace(self):
+ """ Interact with segy in trace mode.
+
+ This mode gives access to reading and writing functionality for traces.
+ The primary data type is the numpy ndarray. Traces can be accessed
+ individually or with python slices, and writing is done via assignment.
+
+ All examples use `np` for `numpy`.
+
+ Examples:
+ Read all traces in file f and store in a list::
+ >>> l = [np.copy(tr) for tr in f.trace]
+
+ Do numpy operations on a trace::
+ >>> tr = f.trace[10]
+ >>> tr = np.transpose(tr)
+ >>> tr = tr * 2
+ >>> tr = tr - 100
+ >>> avg = np.average(tr)
+
+ Do numpy operations on every other trace::
+ >>> for tr in f.trace[::2]:
+ ... print( np.average(tr) )
+ ...
+
+ Traverse traces in reverse::
+ >>> for tr in f.trace[::-1]:
+ ... print( np.average(tr) )
+ ...
+
+ Double every trace value and write to disk. Since accessing a trace
+ gives a numpy value, to write to the respective trace we need its index::
+ >>> for i, tr in enumerate(f.trace):
+ ... tr = tr * 2
+ ... f.trace[i] = tr
+ ...
+
+ Reuse an array for memory efficiency when working with indices.
+ When using slices or full ranges this is done for you::
+ >>> tr = None
+ >>> for i in xrange(100):
+ ... tr = f.trace[i, tr]
+ ... tr = tr * 2
+ ... print(np.average(tr))
+ ...
+
+ Read a value directly from a file. The second [] is numpy access
+ and supports all numpy operations, including negative indexing and
+ slicing::
+ >>> f.trace[0][0]
+ 1490.2
+ >>> f.trace[0][1]
+ 1490.8
+ >>> f.trace[0][-1]
+ 1871.3
+ >>> f.trace[-1][100]
+ 1562.0
+
+ Trace mode supports len(), returning the number of traces in a
+ file::
+ >>> len(f.trace)
+ 300
+
+ Convenient way for setting traces from 0, 1, ... n, based on the
+ iterable set of traces on the right-hand-side.
+
+ If the right-hand-side traces are exhausted before all the destination
+ file traces the writing will stop, i.e. not all all traces in the
+ destination file will be written.
+
+ Copy traces from file f to file g::
+ >>> f.trace = g.trace.
+
+ Copy first half of the traces from g to f::
+ >>> f.trace = g.trace[:len(g.trace)/2]
+
+ Fill the file with one trace (filled with zeros)::
+ >>> tr = np.zeros(f.samples)
+ >>> f.trace = itertools.repeat(tr)
+ """
+ class trace:
+ def __getitem__(inner, index, buf = None):
+ if isinstance(index, tuple):
+ return inner.__getitem__(index[0], index[1])
+
+ buf = self._trace_buffer(buf)
+
+ if isinstance(index, int):
+ if not 0 <= abs(index) < len(inner):
+ raise IndexError("Trace %d not in range (-%d,%d)", (index, len(inner), len(inner)))
+
+ return self._readtr(index, buf)
+
+ elif isinstance(index, slice):
+ def gen():
+ for i in xrange(*index.indices(len(inner))):
+ yield self._readtr(i, buf)
+
+ return gen()
+
+ else:
+ raise TypeError( "Key must be int, slice, (int,np.ndarray) or (slice,np.ndarray)" )
+
+ def __setitem__(inner, index, val):
+ if not 0 <= abs(index) < len(inner):
+ raise IndexError("Trace %d not in range (-%d,%d)", (index, len(inner), len(inner)))
+
+ if not isinstance( val, np.ndarray ):
+ raise TypeError( "Value must be numpy.ndarray" )
+
+ if val.dtype != np.float32:
+ raise TypeError( "Numpy array must be float32" )
+
+ shape = (self.samples,)
+
+ if val.shape[0] < shape[0]:
+ raise TypeError( "Array wrong shape. Expected minimum %s, was %s" % (shape, val.shape))
+
+ if isinstance(index, int):
+ self._writetr(index, val)
+
+ elif isinstance(index, slice):
+ for i, buf in xrange(*index.indices(len(inner))), val:
+ self._writetr(i, val)
+
+ else:
+ raise KeyError( "Wrong shape of index" )
+
+
+ def __len__(inner):
+ return self.tracecount
+
+ def __iter__(inner):
+ return inner[:]
+
+ return trace()
+
+ @trace.setter
+ def trace(self, val):
+ tr = self.trace
+ for i, v in itertools.izip(xrange(len(tr)), val):
+ tr[i] = v
+
+ def _line_buffer(self, length, buf = None):
+ shape = (length, self.samples)
+
+ if buf is None:
+ return np.empty(shape = shape, dtype = np.float32)
+
+ if not isinstance(buf, np.ndarray):
+ return buf
+
+ if buf.dtype != np.float32:
+ return np.empty(shape = shape, dtype = np.float32)
+
+ if buf.shape[0] == shape[0]:
+ return buf
+
+ if buf.shape != shape and buf.size == np.prod(shape):
+ return buf.reshape(shape)
+
+ return buf
+
+ def _fread_trace0(self, lineno, length, stride, linenos, line_type):
+ line0 = ct.c_uint()
+ errc = self._line_trace0(lineno, length, stride, linenos, len(linenos), ct.byref(line0))
+ err = _error(errc)
+
+ if err == _error.OK:
+ return int(line0.value)
+
+ if errc == _error.MISSING_LINE_INDEX:
+ raise KeyError("%s number %d does not exist." % (line_type, lineno))
+
+ errno = ct.get_errno()
+ raise OSError( errno, "Unable to read line %d: %s" % (lineno, os.strerror(errno)))
+
+ def _fread_line(self, trace0, length, stride, buf):
+ errc = self._read_line(trace0, length, stride, _floatp(buf), self._tr0, self._bsz)
+ err = _error(errc)
+
+ if err != _error.OK:
+ errno = ct.get_errno()
+ raise OSError(errno, "Unable to read line starting at trace %d: %s" % (trace0, os.strerror(errno)))
+
+ errc = self._to_native(self._fmt, buf.size, _floatp(buf))
+ err = _error(errc)
+
+ if err == _error.OK:
+ return buf
+
+ raise BufferError("Unable to convert line to native float")
+
+
+ def _fwrite_line(self, trace0, length, stride, buf):
+ errc_conv = self._from_native(self._fmt, buf.size, _floatp(buf))
+ err_conv = _error(errc_conv)
+
+ if err_conv != _error.OK:
+ raise BufferError("Unable to convert line from native float.")
+
+ errc = self._write_line(trace0, length, stride, _floatp(buf), self._tr0, self._bsz)
+ errc_conv = self._to_native(self._fmt, buf.size, _floatp(buf))
+
+ err = _error(errc)
+ err_conv = _error(errc_conv)
+
+ if err != _error.OK:
+ errno = ct.get_errno()
+ raise OSError(errno, "Error writing line starting at trace %d: %s" % (trace0, os.strerror(errno)))
+
+ if err_conv != _error.OK:
+ raise BufferError("Unable to convert line from native float.")
+
+ @property
+ def iline(self):
+ """ Interact with segy in inline mode.
+
+ This mode gives access to reading and writing functionality for inlines.
+ The primary data type is the numpy ndarray. Inlines can be accessed
+ individually or with python slices, and writing is done via assignment.
+ Note that accessing inlines uses the line numbers, not their position,
+ so if a files has inlines [2400..2500], accessing line [0..100] will be
+ an error. Note that each line is returned as a numpy array, meaning
+ accessing the intersections of the inline and crossline is 0-indexed.
+
+ Examples:
+ Read an inline::
+ >>> il = f.iline[2400]
+
+ Copy every inline into a list::
+ >>> l = [np.copy(x) for x in f.iline]
+
+ The number of inlines in a file::
+ >>> len(f.iline)
+
+ Numpy operations on every other inline::
+ >>> for line in f.iline[::2]:
+ ... line = line * 2
+ ... avg = np.average(line)
+ ... print(avg)
+ ...
+
+ Read inlines up to 2430::
+ >>> for line in f.iline[:2430]:
+ ... print(np.average(line))
+ ...
+
+ Copy a line from file g to f::
+ >>> f.iline[2400] = g.iline[2834]
+
+ Copy lines from the first line in g to f, starting at 2400,
+ ending at 2410 in f::
+ >>> f.iline[2400:2411] = g.iline
+
+ Convenient way for setting inlines, from left-to-right as the inline
+ numbers are specified in the file.ilines property, from an iterable
+ set on the right-hand-side.
+
+ If the right-hand-side inlines are exhausted before all the destination
+ file inlines the writing will stop, i.e. not all all inlines in the
+ destination file will be written.
+
+ Copy inlines from file f to file g::
+ >>> f.iline = g.iline.
+
+ Copy first half of the inlines from g to f::
+ >>> f.iline = g.iline[:g.ilines[len(g.ilines)/2]]
+
+ Copy every other inline from a different file::
+ >>> f.iline = g.iline[::2]
+ """
+ il_len, il_stride = self._iline_length, self._iline_stride
+ lines, raw_lines = self.ilines, self._raw_ilines
+ other_lines = self.xlines
+ buffn = lambda x = None: self._line_buffer(il_len, x)
+ readfn = self._fread_line
+
+ def writefn(t0, length, step, val):
+ val = buffn(val)
+ for i, v in itertools.izip(xrange(t0, t0 + step*length, step), val):
+ self._writetr(i, v)
+
+ return _line(self, il_len, il_stride, lines, raw_lines, other_lines, buffn, readfn, writefn, "Inline")
+
+ @iline.setter
+ def iline(self, value):
+ self.iline[:] = value
+
+ @property
+ def xline(self):
+ """ Interact with segy in crossline mode.
+
+ This mode gives access to reading and writing functionality for crosslines.
+ The primary data type is the numpy ndarray. crosslines can be accessed
+ individually or with python slices, and writing is done via assignment.
+ Note that accessing crosslines uses the line numbers, not their position,
+ so if a files has crosslines [1400..1450], accessing line [0..100] will be
+ an error. Note that each line is returned as a numpy array, meaning
+ accessing the intersections of the crossline and crossline is 0-indexed.
+
+ Examples:
+ Read an crossline::
+ >>> il = f.xline[1400]
+
+ Copy every crossline into a list::
+ >>> l = [np.copy(x) for x in f.xline]
+
+ The number of crosslines in a file::
+ >>> len(f.xline)
+
+ Numpy operations on every third crossline::
+ >>> for line in f.xline[::3]:
+ ... line = line * 6
+ ... avg = np.average(line)
+ ... print(avg)
+ ...
+
+ Read crosslines up to 1430::
+ >>> for line in f.xline[:1430]:
+ ... print(np.average(line))
+ ...
+
+ Copy a line from file g to f::
+ >>> f.xline[1400] = g.xline[1603]
+
+ Copy lines from the first line in g to f, starting at 1400,
+ ending at 1415 in f::
+ >>> f.xline[1400:1416] = g.xline
+
+
+ Convenient way for setting crosslines, from left-to-right as the crossline
+ numbers are specified in the file.xlines property, from an iterable
+ set on the right-hand-side.
+
+ If the right-hand-side crosslines are exhausted before all the destination
+ file crosslines the writing will stop, i.e. not all all crosslines in the
+ destination file will be written.
+
+ Copy crosslines from file f to file g::
+ >>> f.xline = g.xline.
+
+ Copy first half of the crosslines from g to f::
+ >>> f.xline = g.xline[:g.xlines[len(g.xlines)/2]]
+
+ Copy every other crossline from a different file::
+ >>> f.xline = g.xline[::2]
+ """
+ xl_len, xl_stride = self._xline_length, self._xline_stride
+ lines, raw_lines = self.xlines, self._raw_xlines
+ other_lines = self.ilines
+ buffn = lambda x = None: self._line_buffer(xl_len, x)
+ readfn = self._fread_line
+
+ def writefn(t0, length, step, val):
+ val = buffn(val)
+ for i, v in itertools.izip(xrange(t0, t0 + step*length, step), val):
+ self._writetr(i, v)
+
+ return _line(self, xl_len, xl_stride, lines, raw_lines, other_lines, buffn, readfn, writefn, "Crossline")
+
+ @xline.setter
+ def xline(self, value):
+ self.xline[:] = value
+
+ def _readh(self, index, buf = None):
+ errc = self._read_header(index, buf, self._tr0, self._bsz)
+ err = _error(errc)
+
+ if err != _error.OK:
+ errno = ct.get_errno()
+ raise OSError(errno, os.strerror(errno))
+
+ return buf
+
+ def _trace_buffer(self, buf = None):
+ samples = self.samples
+
+ if buf is None:
+ buf = np.empty( shape = samples, dtype = np.float32 )
+ elif not isinstance( buf, np.ndarray ):
+ raise TypeError("Buffer must be None or numpy.ndarray" )
+ elif buf.dtype != np.float32:
+ buf = np.empty( shape = samples, dtype = np.float32 )
+ elif buf.shape != samples:
+ buf.reshape( samples )
+
+ return buf
+
+ def _readtr(self, traceno, buf = None):
+ if traceno < 0:
+ traceno += self.tracecount
+
+ buf = self._trace_buffer(buf)
+ bufp = _floatp(buf)
+
+ errc = self._read_trace(traceno, bufp, self._tr0, self._bsz)
+ err = _error(errc)
+
+ if err != _error.OK:
+ errno = ct.get_errno()
+ raise OSError(errno, "Could not read trace %d: %s" % (traceno, os.strerror(errno)))
+
+ errc = self._to_native(self._fmt, self.samples, bufp)
+ err = _error(errc)
+
+ if err == _error.OK:
+ return buf
+
+ raise BufferError("Error converting to native float.")
+
+ def _writetr(self, traceno, buf):
+ bufp = _floatp(buf)
+ errc = self._from_native(self._fmt, self.samples, bufp)
+ err = _error(errc)
+
+ if err != _error.OK:
+ raise BufferError("Error converting from native float.")
+
+ errc = self._write_trace(traceno, bufp, self._tr0, self._bsz)
+ errc_conv = self._to_native(self._fmt, self.samples, bufp)
+
+ err, err_conv = _error(errc), _error(errc_conv)
+
+ if err != _error.OK and err_conv != SEGY_OK:
+ errno = ct.get_errno()
+ raise OSError(errno, "Writing trace failed, and array integrity can not be guaranteed: %s" % os.strerror(errno))
+
+ if err != _error.OK:
+ errno = ct.get_errno()
+ raise OSError(errno, "Error writing trace %d: %s" % (traceno, os.strerror(errno)))
+
+ if err_conv != _error.OK:
+ raise BufferError("Could convert to native float. The array integrety can not be guaranteed.")
+
+ @property
+ def text(self):
+ """ Interact with segy in text mode.
+
+ This mode gives access to reading and writing functionality for textual
+ headers.
+
+ The primary data type is the python string. Reading textual headers is
+ done via [], and writing is done via assignment. No additional
+ structure is built around the textual header, so everything is treated
+ as one long string without line breaks.
+
+ Examples:
+ Print the textual header::
+ >>> print(f.text[0])
+
+ Print the first extended textual header::
+ >>> print(f.text[1])
+
+ Write a new textual header::
+ >>> f.text[0] = make_new_header()
+
+ Print a textual header line-by-line::
+ >>> # using zip, from the zip documentation
+ >>> text = f.text[0]
+ >>> lines = map(''.join, zip( *[iter(text)] * 80))
+ >>> for line in lines:
+ ... print(line)
+ ...
+ """
+ class text:
+ def __init__(inner):
+ inner.size = self._textsize()
+
+ def __getitem__(inner, index):
+ if index > self.ext_headers:
+ raise IndexError("Textual header %d not in file" % index)
+
+ buf = ct.create_string_buffer(inner.size)
+ err = self._texthdr( buf )
+
+ if err == 0: return buf.value
+
+ errno = ct.get_errno()
+ raise OSError(errno, "Could not read text header: %s" % os.strerror(errno))
+
+ def __setitem__(inner, index, val):
+ if index > self.ext_headers:
+ raise IndexError("Textual header %d not in file" % index)
+
+ buf = ct.create_string_buffer(inner.size)
+ for i, x in enumerate(val[:inner.size]):
+ buf[i] = x
+
+ err = self._write_texthdr(index, buf)
+
+ if err == 0: return
+
+ errno = ct.get_errno()
+ raise OSError(errno, "Could not write text header: %s" % os.strerror(errno))
+
+ return text()
+
+ @property
+ def bin(self):
+ """ Interact with segy in binary mode.
+
+ This mode gives access to reading and writing functionality for the
+ binary header. Please note that using numeric binary offsets uses the
+ offset numbers from the specification, i.e. the first field of the
+ binary header starts at 3201, not 1. If you're using the enumerations
+ this is handled for you.
+
+ Examples:
+ Copy a header from file g to file f::
+ >>> f.bin = g.bin
+
+ Reading a field in a trace::
+ >>> traces_per_ensemble = f.bin[3213]
+
+ Writing a field in a trace::
+ >>> f.bin[BinField.Traces] = 5
+
+ Reading multiple fields::
+ >>> d = f.bin[BinField.Traces, 3233]
+
+ Copy a field from file g to file f::
+ >>> f.bin[BinField.Format] = g.bin[BinField.Format]
+
+ Copy full binary from file f to file g::
+ >>> f.bin = g.bin
+
+ Copy multiple fields from file f to file g::
+ >>> f.bin = g.bin[BinField.Traces, 3233]
+
+ Write field in binary header via dict::
+ >>> f.bin = { BinField.Traces: 350 }
+
+ Write multiple fields in a trace::
+ >>> f.bin = { 3213: 5, BinField.SweepFrequencyStart: 17 }
+ """
+ def get_bfield(buf, field, val):
+ errc = self._get_bfield(buf, int(field), ct.byref(val))
+ err = _error(errc)
+
+ if err != _error.OK:
+ raise IndexError("Invalid byte offset %d" % field)
+
+ return int(val.value)
+
+ def set_bfield(buf, field, val):
+ errc = self._set_bfield(buf, int(field), val)
+ err = _error(errc)
+
+ if err != _error.OK:
+ raise IndexError("Invalid byte offset %d" % field)
+
+ buf = (ct.c_char * self._binheader_size())()
+ gt = get_bfield
+ st = set_bfield
+ wr = self._write_binheader
+ ty = BinField
+
+ err = self._binheader(buf)
+ if err == 0:
+ return _header.proxy(buf, get_field = gt, set_field = st, write = wr, field_type = ty)
+
+ errno = ct.get_errno()
+ raise OSError(errno, "Could not read binary header: %s" % os.strerror(errno))
+
+ @bin.setter
+ def bin(self, value):
+ try:
+ buf = self.bin.buf
+
+ except OSError:
+ # the file was probably newly created and the binary header hasn't
+ # been written yet. if this is the case we want to try and write
+ # it. if the file was broken, permissions were wrong etc writing
+ # will fail too
+ buf = (ct.c_char * self._binheader_size())()
+
+ if isinstance(value, dict):
+ for k, v in value.items():
+ self._set_bfield(buf, int(k), v)
+ else:
+ buf = value.buf
+
+ err = self._write_binheader(buf)
+ if err == 0: return
+
+ errno = ct.get_errno()
+ raise OSError(errno, "Could not write text header: %s" % os.strerror(errno))
+
+ @property
+ def format(self):
+ d = {
+ 1: "4-byte IBM float",
+ 2: "4-byte signed integer",
+ 3: "2-byte signed integer",
+ 4: "4-byte fixed point with gain",
+ 5: "4-byte IEEE float",
+ 8: "1-byte signed char"
+ }
+
+ class fmt:
+ def __int__(inner):
+ return self._fmt
+
+ def __str__(inner):
+ if not self._fmt in d:
+ return "Unknown format"
+
+ return d[ self._fmt ]
+
+ return fmt()
+
+ def free(self):
+ """Internal."""
+ pass
+
+class spec:
+ def __init__(self):
+ self.iline = 189
+ self.ilines = None
+ self.xline = 193
+ self.xlines = None
+ self.offsets = 1
+ self.samples = None
+ self.tracecount = None
+ self.ext_headers = 0
+ self.format = None
+ self.sorting = None
+ self.t0 = 1111.0
diff --git a/python/segyio/segysampleformat.py b/python/segyio/segysampleformat.py
new file mode 100644
index 0000000..81abc8a
--- /dev/null
+++ b/python/segyio/segysampleformat.py
@@ -0,0 +1,24 @@
+from cwrap import BaseCEnum
+
+
+class SegySampleFormat(BaseCEnum):
+ TYPE_NAME = "SegySampleFormat"
+
+ IBM_FLOAT_4_BYTE = None
+ SIGNED_INTEGER_4_BYTE = None
+ SIGNED_SHORT_2_BYTE = None
+ FIXED_POINT_WITH_GAIN_4_BYTE = None
+ IEEE_FLOAT_4_BYTE = None
+ NOT_IN_USE_1 = None
+ NOT_IN_USE_2 = None
+ SIGNED_CHAR_1_BYTE = None
+
+SegySampleFormat.addEnum("IBM_FLOAT_4_BYTE", 1)
+SegySampleFormat.addEnum("SIGNED_INTEGER_4_BYTE", 2)
+SegySampleFormat.addEnum("SIGNED_SHORT_2_BYTE", 3)
+SegySampleFormat.addEnum("FIXED_POINT_WITH_GAIN_4_BYTE", 4)
+SegySampleFormat.addEnum("IEEE_FLOAT_4_BYTE", 5)
+SegySampleFormat.addEnum("NOT_IN_USE_1", 6)
+SegySampleFormat.addEnum("NOT_IN_USE_2", 7)
+SegySampleFormat.addEnum("SIGNED_CHAR_1_BYTE", 8)
+
diff --git a/python/segyio/tracefield.py b/python/segyio/tracefield.py
new file mode 100644
index 0000000..5223a1a
--- /dev/null
+++ b/python/segyio/tracefield.py
@@ -0,0 +1,189 @@
+from cwrap import BaseCEnum
+
+
+class TraceField(BaseCEnum):
+ TYPE_NAME = "TraceField"
+
+ TRACE_SEQUENCE_LINE = None
+ TRACE_SEQUENCE_FILE = None
+ FieldRecord = None
+ TraceNumber = None
+ EnergySourcePoint = None
+ CDP = None
+ CDP_TRACE = None
+ TraceIdentificationCode = None
+ NSummedTraces = None
+ NStackedTraces = None
+ DataUse = None
+ offset = None
+ ReceiverGroupElevation = None
+ SourceSurfaceElevation = None
+ SourceDepth = None
+ ReceiverDatumElevation = None
+ SourceDatumElevation = None
+ SourceWaterDepth = None
+ GroupWaterDepth = None
+ ElevationScalar = None
+ SourceGroupScalar = None
+ SourceX = None
+ SourceY = None
+ GroupX = None
+ GroupY = None
+ CoordinateUnits = None
+ WeatheringVelocity = None
+ SubWeatheringVelocity = None
+ SourceUpholeTime = None
+ GroupUpholeTime = None
+ SourceStaticCorrection = None
+ GroupStaticCorrection = None
+ TotalStaticApplied = None
+ LagTimeA = None
+ LagTimeB = None
+ DelayRecordingTime = None
+ MuteTimeStart = None
+ MuteTimeEND = None
+ TRACE_SAMPLE_COUNT = None
+ TRACE_SAMPLE_INTERVAL = None
+ GainType = None
+ InstrumentGainConstant = None
+ InstrumentInitialGain = None
+ Correlated = None
+ SweepFrequencyStart = None
+ SweepFrequencyEnd = None
+ SweepLength = None
+ SweepType = None
+ SweepTraceTaperLengthStart = None
+ SweepTraceTaperLengthEnd = None
+ TaperType = None
+ AliasFilterFrequency = None
+ AliasFilterSlope = None
+ NotchFilterFrequency = None
+ NotchFilterSlope = None
+ LowCutFrequency = None
+ HighCutFrequency = None
+ LowCutSlope = None
+ HighCutSlope = None
+ YearDataRecorded = None
+ DayOfYear = None
+ HourOfDay = None
+ MinuteOfHour = None
+ SecondOfMinute = None
+ TimeBaseCode = None
+ TraceWeightingFactor = None
+ GeophoneGroupNumberRoll1 = None
+ GeophoneGroupNumberFirstTraceOrigField = None
+ GeophoneGroupNumberLastTraceOrigField = None
+ GapSize = None
+ OverTravel = None
+ CDP_X = None
+ CDP_Y = None
+ INLINE_3D = None
+ CROSSLINE_3D = None
+ ShotPoint = None
+ ShotPointScalar = None
+ TraceValueMeasurementUnit = None
+ TransductionConstantMantissa = None
+ TransductionConstantPower = None
+ TransductionUnit = None
+ TraceIdentifier = None
+ ScalarTraceHeader = None
+ SourceType = None
+ SourceEnergyDirectionMantissa = None
+ SourceEnergyDirectionExponent = None
+ SourceMeasurementMantissa = None
+ SourceMeasurementExponent = None
+ SourceMeasurementUnit = None
+ UnassignedInt1 = None
+ UnassignedInt2 = None
+
+TraceField.addEnum("TRACE_SEQUENCE_LINE", 1)
+TraceField.addEnum("TRACE_SEQUENCE_FILE", 5)
+TraceField.addEnum("FieldRecord", 9)
+TraceField.addEnum("TraceNumber", 13)
+TraceField.addEnum("EnergySourcePoint", 17)
+TraceField.addEnum("CDP", 21)
+TraceField.addEnum("CDP_TRACE", 25)
+TraceField.addEnum("TraceIdentificationCode", 29)
+TraceField.addEnum("NSummedTraces", 31)
+TraceField.addEnum("NStackedTraces", 33)
+TraceField.addEnum("DataUse", 35)
+TraceField.addEnum("offset", 37)
+TraceField.addEnum("ReceiverGroupElevation", 41)
+TraceField.addEnum("SourceSurfaceElevation", 45)
+TraceField.addEnum("SourceDepth", 49)
+TraceField.addEnum("ReceiverDatumElevation", 53)
+TraceField.addEnum("SourceDatumElevation", 57)
+TraceField.addEnum("SourceWaterDepth", 61)
+TraceField.addEnum("GroupWaterDepth", 65)
+TraceField.addEnum("ElevationScalar", 69)
+TraceField.addEnum("SourceGroupScalar", 71)
+TraceField.addEnum("SourceX", 73)
+TraceField.addEnum("SourceY", 77)
+TraceField.addEnum("GroupX", 81)
+TraceField.addEnum("GroupY", 85)
+TraceField.addEnum("CoordinateUnits", 89)
+TraceField.addEnum("WeatheringVelocity", 91)
+TraceField.addEnum("SubWeatheringVelocity", 93)
+TraceField.addEnum("SourceUpholeTime", 95)
+TraceField.addEnum("GroupUpholeTime", 97)
+TraceField.addEnum("SourceStaticCorrection", 99)
+TraceField.addEnum("GroupStaticCorrection", 101)
+TraceField.addEnum("TotalStaticApplied", 103)
+TraceField.addEnum("LagTimeA", 105)
+TraceField.addEnum("LagTimeB", 107)
+TraceField.addEnum("DelayRecordingTime", 109)
+TraceField.addEnum("MuteTimeStart", 111)
+TraceField.addEnum("MuteTimeEND", 113)
+TraceField.addEnum("TRACE_SAMPLE_COUNT", 115)
+TraceField.addEnum("TRACE_SAMPLE_INTERVAL", 117)
+TraceField.addEnum("GainType", 119)
+TraceField.addEnum("InstrumentGainConstant", 121)
+TraceField.addEnum("InstrumentInitialGain", 123)
+TraceField.addEnum("Correlated", 125)
+TraceField.addEnum("SweepFrequencyStart", 127)
+TraceField.addEnum("SweepFrequencyEnd", 129)
+TraceField.addEnum("SweepLength", 131)
+TraceField.addEnum("SweepType", 133)
+TraceField.addEnum("SweepTraceTaperLengthStart", 135)
+TraceField.addEnum("SweepTraceTaperLengthEnd", 137)
+TraceField.addEnum("TaperType", 139)
+TraceField.addEnum("AliasFilterFrequency", 141)
+TraceField.addEnum("AliasFilterSlope", 143)
+TraceField.addEnum("NotchFilterFrequency", 145)
+TraceField.addEnum("NotchFilterSlope", 147)
+TraceField.addEnum("LowCutFrequency", 149)
+TraceField.addEnum("HighCutFrequency", 151)
+TraceField.addEnum("LowCutSlope", 153)
+TraceField.addEnum("HighCutSlope", 155)
+TraceField.addEnum("YearDataRecorded", 157)
+TraceField.addEnum("DayOfYear", 159)
+TraceField.addEnum("HourOfDay", 161)
+TraceField.addEnum("MinuteOfHour", 163)
+TraceField.addEnum("SecondOfMinute", 165)
+TraceField.addEnum("TimeBaseCode", 167)
+TraceField.addEnum("TraceWeightingFactor", 169)
+TraceField.addEnum("GeophoneGroupNumberRoll1", 171)
+TraceField.addEnum("GeophoneGroupNumberFirstTraceOrigField", 173)
+TraceField.addEnum("GeophoneGroupNumberLastTraceOrigField", 175)
+TraceField.addEnum("GapSize", 177)
+TraceField.addEnum("OverTravel", 179)
+TraceField.addEnum("CDP_X", 181)
+TraceField.addEnum("CDP_Y", 185)
+TraceField.addEnum("INLINE_3D", 189)
+TraceField.addEnum("CROSSLINE_3D", 193)
+TraceField.addEnum("ShotPoint", 197)
+TraceField.addEnum("ShotPointScalar", 201)
+TraceField.addEnum("TraceValueMeasurementUnit", 203)
+TraceField.addEnum("TransductionConstantMantissa", 205)
+TraceField.addEnum("TransductionConstantPower", 209)
+TraceField.addEnum("TransductionUnit", 211)
+TraceField.addEnum("TraceIdentifier", 213)
+TraceField.addEnum("ScalarTraceHeader", 215)
+TraceField.addEnum("SourceType", 217)
+TraceField.addEnum("SourceEnergyDirectionMantissa", 219)
+TraceField.addEnum("SourceEnergyDirectionExponent", 223)
+TraceField.addEnum("SourceMeasurementMantissa", 225)
+TraceField.addEnum("SourceMeasurementExponent", 229)
+TraceField.addEnum("SourceMeasurementUnit", 231)
+TraceField.addEnum("UnassignedInt1", 233)
+TraceField.addEnum("UnassignedInt2", 237)
\ No newline at end of file
diff --git a/python/segyio/tracesortingformat.py b/python/segyio/tracesortingformat.py
new file mode 100644
index 0000000..3e6846b
--- /dev/null
+++ b/python/segyio/tracesortingformat.py
@@ -0,0 +1,13 @@
+from cwrap import BaseCEnum
+
+
+class TraceSortingFormat(BaseCEnum):
+ TYPE_NAME = "TraceSortingFormat"
+
+ UNKNOWN_SORTING = None
+ CROSSLINE_SORTING = None
+ INLINE_SORTING = None
+
+TraceSortingFormat.addEnum("UNKNOWN_SORTING", -1)
+TraceSortingFormat.addEnum("CROSSLINE_SORTING", 0)
+TraceSortingFormat.addEnum("INLINE_SORTING", 1)
diff --git a/src/applications/segyinfo.c b/src/applications/segyinfo.c
new file mode 100644
index 0000000..e098b40
--- /dev/null
+++ b/src/applications/segyinfo.c
@@ -0,0 +1,141 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include <segyio/segy.h>
+
+static void printSegyTraceInfo( const char* buf ) {
+ int cdp, tsf, xl, il;
+ segy_get_field( buf, CDP, &cdp );
+ segy_get_field( buf, TRACE_SEQUENCE_FILE, &tsf );
+ segy_get_field( buf, CROSSLINE_3D, &xl );
+ segy_get_field( buf, INLINE_3D, &il );
+
+ printf("cdp: %d\n", cdp );
+ printf("TraceSequenceFile: %d\n", tsf );
+ printf("Crossline3D: %d\n", xl );
+ printf("Inline3D: %d\n", il );
+}
+
+static inline int min( int x, int y ) {
+ return x < y ? x : y;
+}
+
+static inline int max( int x, int y ) {
+ return x > y ? x : y;
+}
+
+int main(int argc, char* argv[]) {
+
+ if( argc != 2 ) {
+ puts("Missing argument, expected run signature:");
+ printf(" %s <segy_file>\n", argv[0]);
+ exit(1);
+ }
+
+ FILE* fp = fopen( argv[ 1 ], "r" );
+ if( !fp ) {
+ perror( "fopen():" );
+ exit( 3 );
+ }
+
+ int err;
+ char header[ SEGY_BINARY_HEADER_SIZE ];
+ err = segy_binheader( fp, header );
+
+ if( err != 0 ) {
+ perror( "Unable to read segy binary header:" );
+ exit( err );
+ }
+
+ const int format = segy_format( header );
+ const unsigned int samples = segy_samples( header );
+ const long trace0 = segy_trace0( header );
+ const unsigned int trace_bsize = segy_trace_bsize( samples );
+ int extended_headers;
+ err = segy_get_bfield( header, BIN_ExtendedHeaders, &extended_headers );
+
+ if( err != 0 ) {
+ perror( "Can't read 'extended headers' field from binary header" );
+ exit( err );
+ }
+
+ size_t traces;
+ err = segy_traces( fp, &traces, trace0, trace_bsize );
+
+ if( err != 0 ) {
+ perror( "Could not determine traces" );
+ exit( err );
+ }
+
+ printf( "Sample format: %d\n", format );
+ printf( "Samples per trace: %d\n", samples );
+ printf( "Traces: %zu\n", traces );
+ printf("Extended text header count: %d\n", extended_headers );
+ puts("");
+
+
+ char traceh[ SEGY_TRACE_HEADER_SIZE ];
+ err = segy_traceheader( fp, 0, traceh, trace0, trace_bsize );
+ if( err != 0 ) {
+ perror( "Unable to read trace 0:" );
+ exit( err );
+ }
+
+ puts("Info from first trace:");
+ printSegyTraceInfo( traceh );
+
+ err = segy_traceheader( fp, 1, traceh, trace0, trace_bsize );
+ if( err != 0 ) {
+ perror( "Unable to read trace 1:" );
+ exit( err );
+ }
+
+ puts("");
+ puts("Info from second trace:");
+ printSegyTraceInfo( traceh );
+
+ clock_t start = clock();
+ int min_sample_count = 999999999;
+ int max_sample_count = 0;
+ for( int i = 0; i < (int)traces; i++ ) {
+ err = segy_traceheader( fp, i, traceh, trace0, trace_bsize );
+ if( err != 0 ) {
+ perror( "Unable to read trace" );
+ exit( err );
+ }
+
+ int samples;
+ err = segy_get_field( traceh, TRACE_SAMPLE_COUNT, &samples );
+
+ if( err != 0 ) {
+ fprintf( stderr, "Invalid trace header field: %d\n", TRACE_SAMPLE_COUNT );
+ exit( err );
+ }
+
+ min_sample_count = min( samples, min_sample_count );
+ max_sample_count = max( samples, max_sample_count );
+ }
+
+ puts("");
+ puts("Info from last trace:");
+ err = segy_traceheader( fp, traces - 1, traceh, trace0, trace_bsize );
+
+ if( err != 0 ) {
+ perror( "Unable to read trace." );
+ exit( err );
+ }
+
+ printSegyTraceInfo( traceh );
+
+ puts("");
+ printf("Min sample count: %d\n", min_sample_count);
+ printf("Max sample count: %d\n", max_sample_count);
+ puts("");
+
+ clock_t diff = clock() - start;
+ printf("Read all trace headers in: %.2f s\n", (double) diff / CLOCKS_PER_SEC);
+
+ fclose( fp );
+ return 0;
+}
diff --git a/src/applications/segyinspect.c b/src/applications/segyinspect.c
new file mode 100644
index 0000000..b2c1b4a
--- /dev/null
+++ b/src/applications/segyinspect.c
@@ -0,0 +1,167 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <segyio/segy.h>
+
+
+static const char* getSampleFormatName( int format ) {
+ switch( format ) {
+ case IBM_FLOAT_4_BYTE:
+ return "IBM Float";
+ case SIGNED_INTEGER_4_BYTE:
+ return "Int 32";
+ case SIGNED_SHORT_2_BYTE:
+ return "Int 16";
+ case FIXED_POINT_WITH_GAIN_4_BYTE:
+ return "Fixed Point with gain (Obsolete)";
+ case IEEE_FLOAT_4_BYTE:
+ return "IEEE Float";
+ case NOT_IN_USE_1:
+ return "Not in Use 1";
+ case NOT_IN_USE_2:
+ return "Not in Use 2";
+ case SIGNED_CHAR_1_BYTE:
+ return "Int 8";
+ default:
+ return "Unknown";
+ }
+}
+
+static char* getFastestDirectionName( int sorting ) {
+ if ( sorting == CROSSLINE_SORTING) {
+ return "CROSSLINE";
+ } else {
+ return "INLINE_SORTING";
+ }
+}
+
+int main(int argc, char* argv[]) {
+
+ if (!(argc == 2 || argc == 4)) {
+ puts("Missing argument, expected run signature:");
+ printf(" %s <segy_file> [INLINE_BYTE CROSSLINE_BYTE]\n", argv[0]);
+ printf(" Inline and crossline bytes default to: 189 and 193\n");
+ exit(1);
+ }
+
+ int xl_field = CROSSLINE_3D;
+ int il_field = INLINE_3D;
+
+ if (argc == 4) {
+ il_field = atoi(argv[2]);
+ xl_field = atoi(argv[3]);
+ }
+
+ clock_t start = clock();
+
+ FILE* fp = fopen( argv[ 1 ], "r" );
+ if( !fp ) {
+ perror( "fopen()" );
+ exit( SEGY_FOPEN_ERROR );
+ }
+
+ int err;
+ char header[ SEGY_BINARY_HEADER_SIZE ];
+ err = segy_binheader( fp, header );
+
+ if( err != 0 ) {
+ perror( "Unable to read segy binary header" );
+ exit( err );
+ }
+
+ const int format = segy_format( header );
+ const unsigned int samples = segy_samples( header );
+ const long trace0 = segy_trace0( header );
+ const unsigned int trace_bsize = segy_trace_bsize( samples );
+
+ size_t traces;
+ err = segy_traces( fp, &traces, trace0, trace_bsize );
+
+ if( err != 0 ) {
+ perror( "Could not determine traces" );
+ exit( err );
+ }
+
+ int sorting;
+ err = segy_sorting( fp, il_field, xl_field, &sorting, trace0, trace_bsize );
+ if( err != 0 ) {
+ perror( "Could not determine sorting" );
+ exit( err );
+ }
+
+ unsigned int offsets;
+ err = segy_offsets( fp, il_field, xl_field, traces, &offsets, trace0, trace_bsize );
+ if( err != 0 ) {
+ perror( "Could not determine offsets" );
+ exit( err );
+ }
+
+ unsigned int inline_count, crossline_count;
+ if( sorting == INLINE_SORTING ) {
+ err = segy_count_lines( fp, xl_field, offsets, &inline_count, &crossline_count, trace0, trace_bsize );
+ } else {
+ err = segy_count_lines( fp, il_field, offsets, &crossline_count, &inline_count, trace0, trace_bsize );
+ }
+
+ if( err != 0 ) {
+ fprintf( stderr, "Errcode %d\n", err );
+ perror( "Could not count lines" );
+ exit( err );
+ }
+
+ unsigned int* inline_indices = malloc( sizeof( unsigned int ) * inline_count );
+ unsigned int* crossline_indices = malloc( sizeof( unsigned int ) * crossline_count );
+
+ err = segy_inline_indices( fp, il_field, sorting, inline_count, crossline_count, offsets, inline_indices, trace0, trace_bsize );
+ if( err != 0 ) {
+ perror( "Could not determine inline numbers" );
+ exit( err );
+ }
+
+ err = segy_crossline_indices( fp, xl_field, sorting, inline_count, crossline_count, offsets, crossline_indices, trace0, trace_bsize );
+ if( err != 0 ) {
+ fprintf( stderr, "Errcode %d\n", err );
+ perror( "Could not determine crossline numbers" );
+ exit( err );
+ }
+
+ clock_t diff = clock() - start;
+
+ printf( "Crosslines..........: %d\n", crossline_count);
+ printf( "Inlines.............: %d\n", inline_count);
+ printf( "Offsets.............: %d\n", offsets);
+ printf( "Samples.............: %d\n", samples);
+ printf( "Sample format.......: %s\n", getSampleFormatName( format ) );
+ printf( "Fastest direction...: %s\n", getFastestDirectionName( sorting ) );
+
+
+ puts("");
+ puts("Crossline indexes:");
+
+ for( unsigned int i = 0; i < crossline_count; i++ ) {
+ printf( "%d ", crossline_indices[i] );
+ }
+
+ puts("\n");
+ puts("Inline indexes:");
+
+ for( unsigned int i = 0; i < inline_count; i++ ) {
+ printf( "%d ", inline_indices[i] );
+ }
+
+ puts("\n");
+ puts("Sample indexes:");
+
+ //for (int i = 0; i < spec->sample_count; i++) {
+ // printf("%.2f ", spec->sample_indexes[i]);
+ //}
+ puts("\n");
+
+ printf("Inspection took : %.2f s\n", (double) diff / CLOCKS_PER_SEC);
+
+ free( inline_indices );
+ free( crossline_indices );
+ fclose( fp );
+
+ exit(0);
+}
diff --git a/src/segyio/segy.c b/src/segyio/segy.c
new file mode 100644
index 0000000..5198b7f
--- /dev/null
+++ b/src/segyio/segy.c
@@ -0,0 +1,1131 @@
+#include <netinet/in.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "segy.h"
+
+static unsigned char a2e[256] = {
+ 0, 1, 2, 3, 55, 45, 46, 47, 22, 5, 37, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 60, 61, 50, 38, 24, 25, 63, 39, 28, 29, 30, 31,
+ 64, 79, 127,123,91, 108,80, 125,77, 93, 92, 78, 107,96, 75, 97,
+ 240,241,242,243,244,245,246,247,248,249,122,94, 76, 126,110,111,
+ 124,193,194,195,196,197,198,199,200,201,209,210,211,212,213,214,
+ 215,216,217,226,227,228,229,230,231,232,233,74, 224,90, 95, 109,
+ 121,129,130,131,132,133,134,135,136,137,145,146,147,148,149,150,
+ 151,152,153,162,163,164,165,166,167,168,169,192,106,208,161,7,
+ 32, 33, 34, 35, 36, 21, 6, 23, 40, 41, 42, 43, 44, 9, 10, 27,
+ 48, 49, 26, 51, 52, 53, 54, 8, 56, 57, 58, 59, 4, 20, 62, 225,
+ 65, 66, 67, 68, 69, 70, 71, 72, 73, 81, 82, 83, 84, 85, 86, 87,
+ 88, 89, 98, 99, 100,101,102,103,104,105,112,113,114,115,116,117,
+ 118,119,120,128,138,139,140,141,142,143,144,154,155,156,157,158,
+ 159,160,170,171,172,173,174,175,176,177,178,179,180,181,182,183,
+ 184,185,186,187,188,189,190,191,202,203,204,205,206,207,218,219,
+ 220,221,222,223,234,235,236,237,238,239,250,251,252,253,254,255
+};
+
+static unsigned char e2a[256] = {
+ 0, 1, 2, 3, 156,9, 134,127,151,141,142, 11,12, 13, 14, 15,
+ 16, 17, 18, 19, 157,133,8, 135,24, 25, 146,143,28, 29, 30, 31,
+ 128,129,130,131,132,10, 23, 27, 136,137,138,139,140,5, 6, 7,
+ 144,145,22, 147,148,149,150,4, 152,153,154,155,20, 21, 158,26,
+ 32, 160,161,162,163,164,165,166,167,168,91, 46, 60, 40, 43, 33,
+ 38, 169,170,171,172,173,174,175,176,177,93, 36, 42, 41, 59, 94,
+ 45, 47, 178,179,180,181,182,183,184,185,124,44, 37, 95, 62, 63,
+ 186,187,188,189,190,191,192,193,194,96, 58, 35, 64, 39, 61, 34,
+ 195,97, 98, 99, 100,101,102,103,104,105,196,197,198,199,200,201,
+ 202,106,107,108,109,110,111,112,113,114,203,204,205,206,207,208,
+ 209,126,115,116,117,118,119,120,121,122,210,211,212,213,214,215,
+ 216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,
+ 123,65, 66, 67, 68, 69, 70, 71, 72, 73, 232,233,234,235,236,237,
+ 125,74, 75, 76, 77, 78, 79, 80, 81, 82, 238,239,240,241,242,243,
+ 92, 159,83, 84, 85, 86, 87, 88, 89, 90, 244,245,246,247,248,249,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 250,251,252,253,254,255
+};
+
+void ebcdic2ascii( const char* ebcdic, char* ascii ) {
+ while( *ebcdic != '\0' )
+ *ascii++ = e2a[ (unsigned char) *ebcdic++ ];
+
+ *ascii = '\0';
+}
+
+void ascii2ebcdic( const char* ascii, char* ebcdic ) {
+ while( *ascii != '\0' )
+ *ebcdic++ = a2e[ (unsigned char)*ascii++ ];
+
+ *ebcdic = '\0';
+}
+
+void ibm2ieee(void* to, const void* from, int len) {
+ register unsigned fr; /* fraction */
+ register int exp; /* exponent */
+ register int sgn; /* sign */
+
+ for (; len-- > 0; to = (char*) to + 4, from = (char*) from + 4) {
+ /* split into sign, exponent, and fraction */
+ fr = ntohl(*(int32_t*) from); /* pick up value */
+ sgn = fr >> 31; /* save sign */
+ fr <<= 1; /* shift sign out */
+ exp = fr >> 25; /* save exponent */
+ fr <<= 7; /* shift exponent out */
+
+ if (fr == 0) { /* short-circuit for zero */
+ exp = 0;
+ goto done;
+ }
+
+ /* adjust exponent from base 16 offset 64 radix point before first digit
+ * to base 2 offset 127 radix point after first digit
+ * (exp - 64) * 4 + 127 - 1 == exp * 4 - 256 + 126 == (exp << 2) - 130 */
+ exp = (exp << 2) - 130;
+
+ /* (re)normalize */
+ while (fr < 0x80000000) { /* 3 times max for normalized input */
+ --exp;
+ fr <<= 1;
+ }
+
+ if (exp <= 0) { /* underflow */
+ if (exp < -24) /* complete underflow - return properly signed zero */
+ fr = 0;
+ else /* partial underflow - return denormalized number */
+ fr >>= -exp;
+ exp = 0;
+ } else if (exp >= 255) { /* overflow - return infinity */
+ fr = 0;
+ exp = 255;
+ } else { /* just a plain old number - remove the assumed high bit */
+ fr <<= 1;
+ }
+
+ done:
+ /* put the pieces back together and return it */
+ *(unsigned*) to = (fr >> 9) | (exp << 23) | (sgn << 31);
+ }
+}
+
+void ieee2ibm(void* to, const void* from, int len) {
+ register unsigned fr; /* fraction */
+ register int exp; /* exponent */
+ register int sgn; /* sign */
+
+ for (; len-- > 0; to = (char*) to + 4, from = (char*) from + 4) {
+ /* split into sign, exponent, and fraction */
+ fr = *(unsigned*) from; /* pick up value */
+ sgn = fr >> 31; /* save sign */
+ fr <<= 1; /* shift sign out */
+ exp = fr >> 24; /* save exponent */
+ fr <<= 8; /* shift exponent out */
+
+ if (exp == 255) { /* infinity (or NAN) - map to largest */
+ fr = 0xffffff00;
+ exp = 0x7f;
+ goto done;
+ }
+ else if (exp > 0) /* add assumed digit */
+ fr = (fr >> 1) | 0x80000000;
+ else if (fr == 0) /* short-circuit for zero */
+ goto done;
+
+ /* adjust exponent from base 2 offset 127 radix point after first digit
+ * to base 16 offset 64 radix point before first digit */
+ exp += 130;
+ fr >>= -exp & 3;
+ exp = (exp + 3) >> 2;
+
+ /* (re)normalize */
+ while (fr < 0x10000000) { /* never executed for normalized input */
+ --exp;
+ fr <<= 4;
+ }
+
+ done:
+ /* put the pieces back together and return it */
+ fr = (fr >> 8) | (exp << 24) | (sgn << 31);
+ *(unsigned*) to = htonl(fr);
+ }
+}
+
+static void flipEndianness32(char* data, unsigned int count) {
+ for( unsigned int i = 0; i < count; i += 4) {
+ char a = data[i];
+ char b = data[i + 1];
+ data[i] = data[i + 3];
+ data[i + 1] = data[i + 2];
+ data[i + 2] = b;
+ data[i + 3] = a;
+ }
+}
+
+/* Lookup table for field sizes. All values not explicitly set are 0 */
+static int field_size[] = {
+ [CDP] = 4,
+ [CDP_TRACE] = 4,
+ [CDP_X] = 4,
+ [CDP_Y] = 4,
+ [CROSSLINE_3D] = 4,
+ [EnergySourcePoint] = 4,
+ [FieldRecord] = 4,
+ [GroupWaterDepth] = 4,
+ [GroupX] = 4,
+ [GroupY] = 4,
+ [INLINE_3D] = 4,
+ [offset] = 4,
+ [ReceiverDatumElevation] = 4,
+ [ReceiverGroupElevation] = 4,
+ [ShotPoint] = 4,
+ [SourceDatumElevation] = 4,
+ [SourceDepth] = 4,
+ [SourceEnergyDirectionMantissa] = 4,
+ [SourceMeasurementExponent] = 4,
+ [SourceSurfaceElevation] = 4,
+ [SourceWaterDepth] = 4,
+ [SourceX] = 4,
+ [SourceY] = 4,
+ [TraceNumber] = 4,
+ [TRACE_SEQUENCE_FILE] = 4,
+ [TRACE_SEQUENCE_LINE] = 4,
+ [TransductionConstantMantissa] = 4,
+ [UnassignedInt1] = 4,
+ [UnassignedInt2] = 4,
+
+ [AliasFilterFrequency] = 2,
+ [AliasFilterSlope] = 2,
+ [CoordinateUnits] = 2,
+ [Correlated] = 2,
+ [DataUse] = 2,
+ [DayOfYear] = 2,
+ [DelayRecordingTime] = 2,
+ [ElevationScalar] = 2,
+ [GainType] = 2,
+ [GapSize] = 2,
+ [GeophoneGroupNumberFirstTraceOrigField] = 2,
+ [GeophoneGroupNumberLastTraceOrigField] = 2,
+ [GeophoneGroupNumberRoll1] = 2,
+ [GroupStaticCorrection] = 2,
+ [GroupUpholeTime] = 2,
+ [HighCutFrequency] = 2,
+ [HighCutSlope] = 2,
+ [HourOfDay] = 2,
+ [InstrumentGainConstant] = 2,
+ [InstrumentInitialGain] = 2,
+ [LagTimeA] = 2,
+ [LagTimeB] = 2,
+ [LowCutFrequency] = 2,
+ [LowCutSlope] = 2,
+ [MinuteOfHour] = 2,
+ [MuteTimeEND] = 2,
+ [MuteTimeStart] = 2,
+ [NotchFilterFrequency] = 2,
+ [NotchFilterSlope] = 2,
+ [NStackedTraces] = 2,
+ [NSummedTraces] = 2,
+ [OverTravel] = 2,
+ [ScalarTraceHeader] = 2,
+ [SecondOfMinute] = 2,
+ [ShotPointScalar] = 2,
+ [SourceEnergyDirectionExponent] = 2,
+ [SourceGroupScalar] = 2,
+ [SourceMeasurementMantissa] = 2,
+ [SourceMeasurementUnit] = 2,
+ [SourceStaticCorrection] = 2,
+ [SourceType] = 2,
+ [SourceUpholeTime] = 2,
+ [SubWeatheringVelocity] = 2,
+ [SweepFrequencyEnd] = 2,
+ [SweepFrequencyStart] = 2,
+ [SweepLength] = 2,
+ [SweepTraceTaperLengthEnd] = 2,
+ [SweepTraceTaperLengthStart] = 2,
+ [SweepType] = 2,
+ [TaperType] = 2,
+ [TimeBaseCode] = 2,
+ [TotalStaticApplied] = 2,
+ [TraceIdentificationCode] = 2,
+ [TraceIdentifier] = 2,
+ [TRACE_SAMPLE_COUNT] = 2,
+ [TRACE_SAMPLE_INTERVAL] = 2,
+ [TraceValueMeasurementUnit] = 2,
+ [TraceWeightingFactor] = 2,
+ [TransductionConstantPower] = 2,
+ [TransductionUnit] = 2,
+ [WeatheringVelocity] = 2,
+ [YearDataRecorded] = 2,
+};
+
+#define HEADER_SIZE SEGY_TEXT_HEADER_SIZE
+
+/*
+ * Supporting same byte offsets as in the segy specification, i.e. from the
+ * start of the *text header*, not the binary header.
+ */
+static int bfield_size[] = {
+ [- HEADER_SIZE + BIN_JobID] = 4,
+ [- HEADER_SIZE + BIN_LineNumber] = 4,
+ [- HEADER_SIZE + BIN_ReelNumber] = 4,
+
+ [- HEADER_SIZE + BIN_Traces] = 2,
+ [- HEADER_SIZE + BIN_AuxTraces] = 2,
+ [- HEADER_SIZE + BIN_Interval] = 2,
+ [- HEADER_SIZE + BIN_IntervalOriginal] = 2,
+ [- HEADER_SIZE + BIN_Samples] = 2,
+ [- HEADER_SIZE + BIN_SamplesOriginal] = 2,
+ [- HEADER_SIZE + BIN_Format] = 2,
+ [- HEADER_SIZE + BIN_EnsembleFold] = 2,
+ [- HEADER_SIZE + BIN_SortingCode] = 2,
+ [- HEADER_SIZE + BIN_VerticalSum] = 2,
+ [- HEADER_SIZE + BIN_SweepFrequencyStart] = 2,
+ [- HEADER_SIZE + BIN_SweepFrequencyEnd] = 2,
+ [- HEADER_SIZE + BIN_SweepLength] = 2,
+ [- HEADER_SIZE + BIN_Sweep] = 2,
+ [- HEADER_SIZE + BIN_SweepChannel] = 2,
+ [- HEADER_SIZE + BIN_SweepTaperStart] = 2,
+ [- HEADER_SIZE + BIN_SweepTaperEnd] = 2,
+ [- HEADER_SIZE + BIN_Taper] = 2,
+ [- HEADER_SIZE + BIN_CorrelatedTraces] = 2,
+ [- HEADER_SIZE + BIN_BinaryGainRecovery] = 2,
+ [- HEADER_SIZE + BIN_AmplitudeRecovery] = 2,
+ [- HEADER_SIZE + BIN_MeasurementSystem] = 2,
+ [- HEADER_SIZE + BIN_ImpulseSignalPolarity] = 2,
+ [- HEADER_SIZE + BIN_VibratoryPolarity] = 2,
+
+ [- HEADER_SIZE + BIN_Unassigned1] = 0,
+
+ [- HEADER_SIZE + BIN_SEGYRevision] = 2,
+ [- HEADER_SIZE + BIN_TraceFlag] = 2,
+ [- HEADER_SIZE + BIN_ExtendedHeaders] = 2,
+
+ [- HEADER_SIZE + BIN_Unassigned2] = 0,
+};
+
+/*
+ * to/from_int32 are to be considered internal functions, but have external
+ * linkage so that tests can hook into them. They're not declared in the header
+ * files, and use of this internal interface is at user's own risk, i.e. it may
+ * change without notice.
+ */
+int to_int32( const char* buf ) {
+ int32_t value;
+ memcpy( &value, buf, sizeof( value ) );
+ return ((value >> 24) & 0xff)
+ | ((value << 8) & 0xff0000)
+ | ((value >> 8) & 0xff00)
+ | ((value << 24) & 0xff000000);
+}
+
+int to_int16( const char* buf ) {
+ int16_t value;
+ memcpy( &value, buf, sizeof( value ) );
+ return ((value >> 8) & 0x00ff)
+ | ((value << 8) & 0xff00);
+}
+
+/* from native int to segy int. fixed-width ints used as byte buffers */
+int32_t from_int32( int32_t buf ) {
+ int32_t value = 0;
+ memcpy( &value, &buf, sizeof( value ) );
+ return ((value >> 24) & 0xff)
+ | ((value << 8) & 0xff0000)
+ | ((value >> 8) & 0xff00)
+ | ((value << 24) & 0xff000000);
+}
+
+int16_t from_int16( int16_t buf ) {
+ int16_t value = 0;
+ memcpy( &value, &buf, sizeof( value ) );
+ return ((value >> 8) & 0x00ff)
+ | ((value << 8) & 0xff00);
+}
+
+static int get_field( const char* header,
+ const int* table,
+ int field,
+ int* f ) {
+
+ const int bsize = table[ field ];
+
+ switch( bsize ) {
+ case 4:
+ *f = to_int32( header + (field - 1) );
+ return SEGY_OK;
+
+ case 2:
+ *f = to_int16( header + (field - 1) );
+ return SEGY_OK;
+
+ case 0:
+ default:
+ return SEGY_INVALID_FIELD;
+ }
+}
+
+int segy_get_field( const char* traceheader, int field, int* f ) {
+ if( field < 0 || field >= SEGY_TRACE_HEADER_SIZE )
+ return SEGY_INVALID_FIELD;
+
+ return get_field( traceheader, field_size, field, f );
+}
+
+int segy_get_bfield( const char* binheader, int field, int* f ) {
+ field -= SEGY_TEXT_HEADER_SIZE;
+
+ if( field < 0 || field >= SEGY_BINARY_HEADER_SIZE )
+ return SEGY_INVALID_FIELD;
+
+ return get_field( binheader, bfield_size, field, f );
+}
+
+static int set_field( char* header, const int* table, int field, int val ) {
+ const int bsize = table[ field ];
+
+ int32_t buf32;
+ int16_t buf16;
+
+ switch( bsize ) {
+ case 4:
+ buf32 = from_int32( val );
+ memcpy( header + (field - 1), &buf32, sizeof( buf32 ) );
+ return SEGY_OK;
+
+ case 2:
+ buf16 = from_int16( val );
+ memcpy( header + (field - 1), &buf16, sizeof( buf16 ) );
+ return SEGY_OK;
+
+ case 0:
+ default:
+ return SEGY_INVALID_FIELD;
+ }
+}
+
+int segy_set_field( char* traceheader, int field, int val ) {
+ if( field < 0 || field >= SEGY_TRACE_HEADER_SIZE )
+ return SEGY_INVALID_FIELD;
+
+ return set_field( traceheader, field_size, field, val );
+}
+
+int segy_set_bfield( char* binheader, int field, int val ) {
+ field -= SEGY_TEXT_HEADER_SIZE;
+
+ if( field < 0 || field >= SEGY_BINARY_HEADER_SIZE )
+ return SEGY_INVALID_FIELD;
+
+ return set_field( binheader, bfield_size, field, val );
+}
+
+int segy_binheader( FILE* fp, char* buf ) {
+ const int err = fseek( fp, SEGY_TEXT_HEADER_SIZE, SEEK_SET );
+ if( err != 0 ) return SEGY_FSEEK_ERROR;
+
+ const size_t read_count = fread( buf, 1, SEGY_BINARY_HEADER_SIZE, fp);
+ if( read_count != SEGY_BINARY_HEADER_SIZE )
+ return SEGY_FREAD_ERROR;
+
+ return SEGY_OK;
+}
+
+int segy_write_binheader( FILE* fp, const char* buf ) {
+ const int err = fseek( fp, SEGY_TEXT_HEADER_SIZE, SEEK_SET );
+ if( err != 0 ) return SEGY_FSEEK_ERROR;
+
+ const size_t writec = fwrite( buf, 1, SEGY_BINARY_HEADER_SIZE, fp);
+ if( writec != SEGY_BINARY_HEADER_SIZE )
+ return SEGY_FREAD_ERROR;
+
+ return SEGY_OK;
+}
+
+int segy_format( const char* buf ) {
+ int format;
+ segy_get_bfield( buf, BIN_Format, &format );
+ return format;
+}
+
+unsigned int segy_samples( const char* buf ) {
+ int samples;
+ segy_get_bfield( buf, BIN_Samples, &samples );
+ return samples;
+}
+
+unsigned int segy_trace_bsize( unsigned int samples ) {
+ /* Hard four-byte float assumption */
+ return samples * 4;
+}
+
+long segy_trace0( const char* binheader ) {
+ int extra_headers;
+ segy_get_bfield( binheader, BIN_ExtendedHeaders, &extra_headers );
+
+ return SEGY_TEXT_HEADER_SIZE + SEGY_BINARY_HEADER_SIZE +
+ SEGY_TEXT_HEADER_SIZE * extra_headers;
+}
+
+int segy_seek( FILE* fp,
+ unsigned int trace,
+ long trace0,
+ unsigned int trace_bsize ) {
+
+ trace_bsize += SEGY_TRACE_HEADER_SIZE;
+ const long pos = trace0 + ( (long)trace * (long)trace_bsize );
+ const int err = fseek( fp, pos, SEEK_SET );
+ if( err != 0 ) return SEGY_FSEEK_ERROR;
+ return SEGY_OK;
+}
+
+int segy_traceheader( FILE* fp,
+ unsigned int traceno,
+ char* buf,
+ long trace0,
+ unsigned int trace_bsize ) {
+
+ const int err = segy_seek( fp, traceno, trace0, trace_bsize );
+ if( err != 0 ) return err;
+
+ const size_t readc = fread( buf, 1, SEGY_TRACE_HEADER_SIZE, fp );
+
+ if( readc != SEGY_TRACE_HEADER_SIZE )
+ return SEGY_FREAD_ERROR;
+
+ return SEGY_OK;
+}
+
+int segy_write_traceheader( FILE* fp,
+ unsigned int traceno,
+ const char* buf,
+ long trace0,
+ unsigned int trace_bsize ) {
+
+ const int err = segy_seek( fp, traceno, trace0, trace_bsize );
+ if( err != 0 ) return err;
+
+ const size_t writec = fwrite( buf, 1, SEGY_TRACE_HEADER_SIZE, fp );
+
+ if( writec != SEGY_TRACE_HEADER_SIZE )
+ return SEGY_FWRITE_ERROR;
+
+ return SEGY_OK;
+}
+
+/*
+ * Determine the file size in bytes. If this function succeeds, the file
+ * pointer will be reset to wherever it was before this call. If this call
+ * fails for some reason, the return value is 0 and the file pointer location
+ * will be determined by the behaviour of fseek.
+ */
+static int file_size( FILE* fp, size_t* size ) {
+ const long prev_pos = ftell( fp );
+
+ int err = fseek( fp, 0, SEEK_END );
+ if( err != 0 ) return SEGY_FSEEK_ERROR;
+
+ const size_t sz = ftell( fp );
+ err = fseek( fp, prev_pos, SEEK_SET );
+ if( err != 0 ) return SEGY_FSEEK_ERROR;
+
+ *size = sz;
+ return SEGY_OK;
+}
+
+/*
+ * Return the number of traces in the file. The file pointer won't change after
+ * this call unless fseek itself fails.
+ *
+ * This function assumes that *all traces* are of the same size.
+ */
+int segy_traces( FILE* fp, size_t* traces, long trace0, unsigned int trace_bsize ) {
+
+ size_t fsize;
+ int err = file_size( fp, &fsize );
+ if( err != 0 ) return err;
+
+ trace_bsize += SEGY_TRACE_HEADER_SIZE;
+ const size_t trace_data_size = fsize - trace0;
+
+ if( trace_data_size % trace_bsize != 0 )
+ return SEGY_TRACE_SIZE_MISMATCH;
+
+ *traces = trace_data_size / trace_bsize;
+ return SEGY_OK;
+}
+
+static int segy_sample_interval(FILE* fp, double* dt) {
+
+ char bin_header[ SEGY_BINARY_HEADER_SIZE ];
+ char trace_header[SEGY_TRACE_HEADER_SIZE];
+
+ int err = segy_binheader( fp, bin_header );
+ if (err != 0) {
+ return err;
+ }
+
+ const long trace0 = segy_trace0( bin_header );
+ unsigned int samples = segy_samples( bin_header );
+ const size_t trace_bsize = segy_trace_bsize( samples );
+
+ err = segy_traceheader(fp, 0, trace_header, trace0, trace_bsize);
+ if (err != 0) {
+ return err;
+ }
+
+ int _binary_header_dt;
+ segy_get_bfield(bin_header, BIN_Interval, &_binary_header_dt);
+ double binary_header_dt = _binary_header_dt/1000.0;
+
+ int _trace_header_dt;
+ segy_get_field(trace_header, TRACE_SAMPLE_INTERVAL, &_trace_header_dt);
+ double trace_header_dt = _trace_header_dt/1000.0;
+
+
+ *dt = trace_header_dt;
+
+ if (binary_header_dt == 0.0 && trace_header_dt == 0.0) {
+ fprintf(stderr, "Trace sampling rate in SEGY header and trace header set to 0.0. Will default to 4 ms.\n");
+ *dt = 4.0;
+ } else if (binary_header_dt == 0.0) {
+ fprintf(stderr, "Trace sampling rate in SEGY header set to 0.0. Will use trace sampling rate of: %f\n", trace_header_dt);
+ *dt = trace_header_dt;
+ } else if (trace_header_dt == 0.0) {
+ fprintf(stderr, "Trace sampling rate in trace header set to 0.0. Will use SEGY header sampling rate of: %f\n", binary_header_dt);
+ *dt = binary_header_dt;
+ } else if (trace_header_dt != binary_header_dt) {
+ fprintf(stderr, "Trace sampling rate in SEGY header and trace header are not equal. Will use SEGY header sampling rate of: %f\n",
+ binary_header_dt);
+ *dt = binary_header_dt;
+ }
+
+ return 0;
+
+}
+
+int segy_sample_indexes(FILE* fp, double* buf, double t0, size_t count) {
+
+ double dt;
+ int err = segy_sample_interval(fp, &dt);
+ if (err != 0) {
+ return err;
+ }
+
+ for (int i = 0; i < count; i++) {
+ buf[i] = t0 + i * dt;
+ }
+
+ return 0;
+
+}
+
+/*
+ * Determine how a file is sorted. Expects the following two fields from the
+ * trace header to guide sorting: the inline number `il` and the crossline
+ * number `xl`.
+ *
+ * Inspects trace headers 0 and 1 and compares these two fields in the
+ * respective trace headers. If the first two traces are components of the same
+ * inline, header[0].ilnum should be equal to header[1].ilnum, similarly for
+ * crosslines. If neither match, the sorting is considered unknown.
+ */
+int segy_sorting( FILE* fp,
+ int il,
+ int xl,
+ int* sorting,
+ long trace0,
+ unsigned int trace_bsize ) {
+ int err;
+ char traceheader[ SEGY_TRACE_HEADER_SIZE ];
+
+ err = segy_traceheader( fp, 0, traceheader, trace0, trace_bsize );
+ if( err != SEGY_OK ) return err;
+
+ /* make sure field is valid, so we don't have to check errors later */
+ if( field_size[ il ] == 0 || field_size[ xl ] == 0 )
+ return SEGY_INVALID_FIELD;
+
+ int il0, xl0, il1, xl1, off0, off1;
+
+ segy_get_field( traceheader, il, &il0 );
+ segy_get_field( traceheader, xl, &xl0 );
+ segy_get_field( traceheader, offset, &off0 );
+
+ int traceno = 1;
+
+ do {
+ err = segy_traceheader( fp, traceno, traceheader, trace0, trace_bsize );
+ if( err != SEGY_OK ) return err;
+
+ segy_get_field( traceheader, il, &il1 );
+ segy_get_field( traceheader, xl, &xl1 );
+ segy_get_field( traceheader, offset, &off1 );
+ ++traceno;
+ } while( off0 != off1 );
+
+ if ( il0 == il1 ) *sorting = INLINE_SORTING;
+ else if( xl0 == xl1 ) *sorting = CROSSLINE_SORTING;
+ else return SEGY_INVALID_SORTING;
+
+ return SEGY_OK;
+}
+
+/*
+ * Find the number of offsets. This is determined by inspecting the trace
+ * headers [0,n) where n is the first trace where either the inline number or
+ * the crossline number changes (which changes first depends on sorting, but is
+ * irrelevant for this function).
+ */
+int segy_offsets( FILE* fp,
+ int il,
+ int xl,
+ unsigned int traces,
+ unsigned int* out,
+ long trace0,
+ unsigned int trace_bsize ) {
+ int err;
+ int il0, il1, xl0, xl1;
+ char header[ SEGY_TRACE_HEADER_SIZE ];
+ unsigned int offsets = 0;
+
+ do {
+ err = segy_traceheader( fp, offsets, header, trace0, trace_bsize );
+ if( err != 0 ) return err;
+
+ /*
+ * check that field value is sane, so that we don't have to check
+ * segy_get_field's error
+ */
+ if( field_size[ il ] == 0 || field_size[ xl ] == 0 )
+ return SEGY_INVALID_FIELD;
+
+ segy_get_field( header, il, &il0 );
+ segy_get_field( header, xl, &xl0 );
+
+ err = segy_traceheader( fp, offsets + 1, header, trace0, trace_bsize );
+ if( err != 0 ) return err;
+ segy_get_field( header, il, &il1 );
+ segy_get_field( header, xl, &xl1 );
+
+ ++offsets;
+ } while( il0 == il1 && xl0 == xl1 && offsets < traces );
+
+ if( offsets >= traces ) return SEGY_INVALID_OFFSETS;
+
+ *out = offsets;
+ return SEGY_OK;
+}
+
+int segy_line_indices( FILE* fp,
+ int field,
+ unsigned int traceno,
+ unsigned int stride,
+ unsigned int num_indices,
+ unsigned int* buf,
+ long trace0,
+ unsigned int trace_bsize ) {
+
+ if( field_size[ field ] == 0 )
+ return SEGY_INVALID_FIELD;
+
+ char header[ SEGY_TRACE_HEADER_SIZE ];
+ for( ; num_indices--; traceno += stride, ++buf ) {
+
+ int err = segy_traceheader( fp, traceno, header, trace0, trace_bsize );
+ if( err != 0 ) return SEGY_FREAD_ERROR;
+
+ segy_get_field( header, field, (int*)buf );
+ }
+
+ return SEGY_OK;
+}
+
+static int count_lines( FILE* fp,
+ int field,
+ unsigned int offsets,
+ unsigned int* out,
+ long trace0,
+ unsigned int trace_bsize ) {
+
+ int err;
+ char header[ SEGY_TRACE_HEADER_SIZE ];
+ err = segy_traceheader( fp, 0, header, trace0, trace_bsize );
+ if( err != 0 ) return err;
+
+ int first_lineno, first_offset, ln, off;
+
+ err = segy_get_field( header, field, &first_lineno );
+ if( err != 0 ) return err;
+
+ err = segy_get_field( header, 37, &first_offset );
+ if( err != 0 ) return err;
+
+ unsigned int lines = 1;
+ unsigned int curr = offsets;
+
+ while( true ) {
+ err = segy_traceheader( fp, curr, header, trace0, trace_bsize );
+ if( err != 0 ) return err;
+
+ segy_get_field( header, field, &ln );
+ segy_get_field( header, 37, &off );
+
+ if( first_offset == off && ln == first_lineno ) break;
+
+ curr += offsets;
+ ++lines;
+ }
+
+ *out = lines;
+ return SEGY_OK;
+}
+
+int segy_count_lines( FILE* fp,
+ int field,
+ unsigned int offsets,
+ unsigned int* l1out,
+ unsigned int* l2out,
+ long trace0,
+ unsigned int trace_bsize ) {
+
+ int err;
+ unsigned int l2count;
+ err = count_lines( fp, field, offsets, &l2count, trace0, trace_bsize );
+ if( err != 0 ) return err;
+
+ size_t traces;
+ err = segy_traces( fp, &traces, trace0, trace_bsize );
+ if( err != 0 ) return err;
+
+ const unsigned int line_length = l2count * offsets;
+ const unsigned int l1count = traces / line_length;
+
+ *l1out = l1count;
+ *l2out = l2count;
+
+ return SEGY_OK;
+}
+
+int segy_inline_length( int sorting,
+ unsigned int traces,
+ unsigned int crossline_count,
+ unsigned int offsets,
+ unsigned int* line_length ) {
+
+ if( sorting == INLINE_SORTING ) {
+ *line_length = crossline_count;
+ return SEGY_OK;
+ }
+
+ if( sorting == CROSSLINE_SORTING ) {
+ *line_length = traces / (crossline_count * offsets);
+ return SEGY_OK;
+ }
+
+ return SEGY_INVALID_SORTING;
+}
+
+int segy_crossline_length( int sorting,
+ unsigned int traces,
+ unsigned int inline_count,
+ unsigned int offsets,
+ unsigned int* line_length ) {
+
+ if( sorting == INLINE_SORTING ) {
+ *line_length = inline_count;
+ return SEGY_OK;
+ }
+
+ if( sorting == CROSSLINE_SORTING ) {
+ *line_length = traces / (inline_count * offsets);
+ return SEGY_OK;
+ }
+
+ return SEGY_INVALID_SORTING;
+}
+
+int segy_inline_indices( FILE* fp,
+ int il,
+ int sorting,
+ unsigned int inline_count,
+ unsigned int crossline_count,
+ unsigned int offsets,
+ unsigned int* buf,
+ long trace0,
+ unsigned int trace_bsize) {
+ int err;
+
+ if( sorting == INLINE_SORTING ) {
+ size_t traces;
+ err = segy_traces( fp, &traces, trace0, trace_bsize );
+ if( err != 0 ) return err;
+
+ unsigned int stride = crossline_count * offsets;
+ return segy_line_indices( fp, il, 0, stride, inline_count, buf, trace0, trace_bsize );
+ }
+
+ if( sorting == CROSSLINE_SORTING ) {
+ return segy_line_indices( fp, il, 0, offsets, inline_count, buf, trace0, trace_bsize );
+ }
+
+ return SEGY_INVALID_SORTING;
+}
+
+int segy_crossline_indices( FILE* fp,
+ int xl,
+ int sorting,
+ unsigned int inline_count,
+ unsigned int crossline_count,
+ unsigned int offsets,
+ unsigned int* buf,
+ long trace0,
+ unsigned int trace_bsize ) {
+
+ int err;
+
+ if( sorting == INLINE_SORTING ) {
+ return segy_line_indices( fp, xl, 0, offsets, crossline_count, buf, trace0, trace_bsize );
+ }
+
+ if( sorting == CROSSLINE_SORTING ) {
+ size_t traces;
+ err = segy_traces( fp, &traces, trace0, trace_bsize );
+ if( err != 0 ) return err;
+
+ unsigned int stride = inline_count * offsets;
+ return segy_line_indices( fp, xl, 0, stride, crossline_count, buf, trace0, trace_bsize );
+ }
+
+ return SEGY_INVALID_SORTING;
+}
+
+
+static int skip_traceheader( FILE* fp ) {
+ const int err = fseek( fp, SEGY_TRACE_HEADER_SIZE, SEEK_CUR );
+ if( err != 0 ) return SEGY_FSEEK_ERROR;
+ return SEGY_OK;
+}
+
+int segy_readtrace( FILE* fp,
+ unsigned int traceno,
+ float* buf,
+ long trace0,
+ unsigned int trace_bsize ) {
+ int err;
+ err = segy_seek( fp, traceno, trace0, trace_bsize );
+ if( err != 0 ) return err;
+
+ err = skip_traceheader( fp );
+ if( err != 0 ) return err;
+
+ const size_t readc = fread( buf, 1, trace_bsize, fp );
+ if( readc != trace_bsize ) return SEGY_FREAD_ERROR;
+
+ return SEGY_OK;
+
+}
+
+int segy_writetrace( FILE* fp,
+ unsigned int traceno,
+ const float* buf,
+ long trace0,
+ unsigned int trace_bsize ) {
+
+ int err;
+ err = segy_seek( fp, traceno, trace0, trace_bsize );
+ if( err != 0 ) return err;
+
+ err = skip_traceheader( fp );
+ if( err != 0 ) return err;
+
+ const size_t writec = fwrite( buf, 1, trace_bsize, fp );
+ if( writec != trace_bsize )
+ return SEGY_FWRITE_ERROR;
+ return SEGY_OK;
+}
+
+int segy_to_native( int format,
+ unsigned int size,
+ float* buf ) {
+
+ if( format == IEEE_FLOAT_4_BYTE )
+ flipEndianness32( (char*)buf, size * sizeof( float ) );
+ else
+ ibm2ieee( buf, buf, size );
+
+ return SEGY_OK;
+}
+
+int segy_from_native( int format,
+ unsigned int size,
+ float* buf ) {
+
+ if( format == IEEE_FLOAT_4_BYTE )
+ flipEndianness32( (char*)buf, size * sizeof( float ) );
+ else
+ ieee2ibm( buf, buf, size );
+
+ return SEGY_OK;
+}
+
+/*
+ * Determine the position of the element `x` in `xs`.
+ * Returns -1 if the value cannot be found
+ */
+static long index_of( unsigned int x,
+ const unsigned int* xs,
+ unsigned int sz ) {
+ for( unsigned int i = 0; i < sz; i++ ) {
+ if( xs[i] == x )
+ return i;
+ }
+
+ return -1;
+}
+
+/*
+ * Read the inline or crossline `lineno`. If it's an inline or crossline
+ * depends on the parameters. The line has a length of `line_length` traces,
+ * and `buf` must be of (minimum) `line_length*samples_per_trace` size. Reads
+ * every `stride` trace, starting at the trace specified by the *position* of
+ * the value `lineno` in `linenos`. If `lineno` isn't present in `linenos`,
+ * SEGY_MISSING_LINE_INDEX will be returned.
+ *
+ * If reading a trace fails, this function will return whatever error
+ * segy_readtrace returns.
+ */
+int segy_read_line( FILE* fp,
+ unsigned int line_trace0,
+ unsigned int line_length,
+ unsigned int stride,
+ float* buf,
+ long trace0,
+ unsigned int trace_bsize ) {
+
+ const size_t trace_data_size = trace_bsize / 4;
+
+ for( ; line_length--; line_trace0 += stride, buf += trace_data_size ) {
+ int err = segy_readtrace( fp, line_trace0, buf, trace0, trace_bsize );
+ if( err != 0 ) return err;
+ }
+
+ return SEGY_OK;
+}
+
+/*
+ * Write the inline or crossline `lineno`. If it's an inline or crossline
+ * depends on the parameters. The line has a length of `line_length` traces,
+ * and `buf` must be of (minimum) `line_length*samples_per_trace` size. Reads
+ * every `stride` trace, starting at the trace specified by the *position* of
+ * the value `lineno` in `linenos`. If `lineno` isn't present in `linenos`,
+ * SEGY_MISSING_LINE_INDEX will be returned.
+ *
+ * If reading a trace fails, this function will return whatever error
+ * segy_readtrace returns.
+ */
+int segy_write_line( FILE* fp,
+ unsigned int line_trace0,
+ unsigned int line_length,
+ unsigned int stride,
+ float* buf,
+ long trace0,
+ unsigned int trace_bsize ) {
+
+ const size_t trace_data_size = trace_bsize / 4;
+
+ for( ; line_length--; line_trace0 += stride, buf += trace_data_size ) {
+ int err = segy_writetrace( fp, line_trace0, buf, trace0, trace_bsize );
+ if( err != 0 ) return err;
+ }
+
+ return SEGY_OK;
+}
+
+
+int segy_line_trace0( unsigned int lineno,
+ unsigned int line_length,
+ unsigned int stride,
+ const unsigned int* linenos,
+ const unsigned int linenos_sz,
+ unsigned int* traceno ) {
+
+ long index = index_of( lineno, linenos, linenos_sz );
+
+ if( index < 0 ) return SEGY_MISSING_LINE_INDEX;
+
+ if( stride == 1 ) index *= line_length;
+
+ *traceno = index;
+
+ return SEGY_OK;
+}
+
+int segy_inline_stride( int sorting,
+ unsigned int inline_count,
+ unsigned int* stride ) {
+ switch( sorting ) {
+ case CROSSLINE_SORTING:
+ *stride = inline_count;
+ return SEGY_OK;
+
+ case INLINE_SORTING:
+ *stride = 1;
+ return SEGY_OK;
+
+ default:
+ return SEGY_INVALID_SORTING;
+ }
+}
+
+int segy_crossline_stride( int sorting,
+ unsigned int crossline_count,
+ unsigned int* stride ) {
+ switch( sorting ) {
+ case CROSSLINE_SORTING:
+ *stride = 1;
+ return SEGY_OK;
+
+ case INLINE_SORTING:
+ *stride = crossline_count;
+ return SEGY_OK;
+
+ default:
+ return SEGY_INVALID_SORTING;
+ }
+}
+
+int segy_textheader( FILE* fp, char* buf ) {
+ rewind( fp );
+
+ const size_t read = fread( buf, 1, SEGY_TEXT_HEADER_SIZE, fp );
+ if( read != SEGY_TEXT_HEADER_SIZE ) return SEGY_FREAD_ERROR;
+
+ buf[ SEGY_TEXT_HEADER_SIZE ] = '\0';
+ ebcdic2ascii( buf, buf );
+ return SEGY_OK;
+}
+
+int segy_write_textheader( FILE* fp, unsigned int pos, const char* buf ) {
+ int err;
+ char mbuf[ SEGY_TEXT_HEADER_SIZE + 1 ];
+
+ // TODO: reconsider API, allow non-zero terminated strings
+ ascii2ebcdic( buf, mbuf );
+
+ const long offset = pos == 0
+ ? 0
+ : SEGY_TEXT_HEADER_SIZE + SEGY_BINARY_HEADER_SIZE +
+ ((pos-1) * SEGY_TEXT_HEADER_SIZE);
+
+ err = fseek( fp, offset, SEEK_SET );
+ if( err != 0 ) return SEGY_FSEEK_ERROR;
+
+ size_t writec = fwrite( mbuf, 1, SEGY_TEXT_HEADER_SIZE, fp );
+ if( writec != SEGY_TEXT_HEADER_SIZE )
+ return SEGY_FWRITE_ERROR;
+
+ return SEGY_OK;
+}
+
+unsigned int segy_textheader_size() {
+ return SEGY_TEXT_HEADER_SIZE + 1;
+}
+
+unsigned int segy_binheader_size() {
+ return SEGY_BINARY_HEADER_SIZE;
+}
diff --git a/src/segyio/segy.h b/src/segyio/segy.h
new file mode 100644
index 0000000..17b3388
--- /dev/null
+++ b/src/segyio/segy.h
@@ -0,0 +1,393 @@
+#ifndef SEGYIO_SEGY_H
+#define SEGYIO_SEGY_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#define SEGY_BINARY_HEADER_SIZE 400
+#define SEGY_TEXT_HEADER_SIZE 3200
+#define SEGY_TRACE_HEADER_SIZE 240
+
+/*
+ * About signatures:
+ * If a function returns `int` you can assume the return value is an error
+ * code. 0 will always indicate success. If a function returns something else
+ * than an int it's typically an operation that cannot fail assuming the passed
+ * buffer is of the correct size. Any exceptions will be clearly stated.
+ *
+ * Function signatures are typically:
+ * 1) input parameters
+ * 2) output parameters
+ * 3) low-level file structure information
+ *
+ * Output parameters are non-const pointers, input parameters are const
+ * pointers or plain values. All functions are namespace-prefix'd with segy_.
+ * Some functions return values, notably the family concerned with the binary
+ * header such as segy_trace0, that should be used in consecutive segy function
+ * calls that use the same name for one of its parameters.
+ */
+
+/* binary header operations */
+/*
+ * The binheader buffer passed to these functions must be of *at least*
+ * `segy_binheader_size`.
+ */
+unsigned int segy_binheader_size();
+int segy_binheader( FILE*, char* buf );
+int segy_write_binheader( FILE*, const char* buf );
+unsigned int segy_samples( const char* binheader );
+/* exception: the int returned is an enum, SEGY_SORTING, not an error code */
+int segy_format( const char* binheader );
+int segy_get_field( const char* traceheader, int field, int* f );
+int segy_get_bfield( const char* binheader, int field, int* f );
+int segy_set_field( char* traceheader, int field, int val );
+int segy_set_bfield( char* binheader, int field, int val );
+
+unsigned segy_trace_bsize( unsigned int samples );
+/* byte-offset of the first trace header. */
+long segy_trace0( const char* binheader );
+/* number of traces in this file */
+int segy_traces( FILE*, size_t*, long trace0, unsigned int trace_bsize );
+
+int segy_sample_indexes(FILE* fp, double* buf, double t0, size_t count);
+
+/* text header operations */
+int segy_textheader( FILE*, char* buf );
+unsigned int segy_textheader_size();
+int segy_write_textheader( FILE*, unsigned int pos, const char* buf );
+
+/* Read the trace header at `traceno` into `buf`. */
+int segy_traceheader( FILE*,
+ unsigned int traceno,
+ char* buf,
+ long trace0,
+ unsigned int trace_bsize );
+
+/* Read the trace header at `traceno` into `buf`. */
+int segy_write_traceheader( FILE*,
+ unsigned int traceno,
+ const char* buf,
+ long trace0,
+ unsigned int trace_bsize );
+
+/*
+ * Number of traces in this file. The sorting type will be written to `sorting`
+ * if the function can figure out how the file is sorted. If not, some error
+ * code will be returned and `out` will not be modified.
+ */
+int segy_sorting( FILE*,
+ int il,
+ int xl,
+ int* sorting,
+ long trace0,
+ unsigned int trace_bsize );
+
+/*
+ * Number of offsets in this file, written to `offsets`. 1 if a 3D data set, >1
+ * if a 4D data set.
+ */
+int segy_offsets( FILE*,
+ int il,
+ int xl,
+ unsigned int traces,
+ unsigned int* offsets,
+ long trace0,
+ unsigned int trace_bsize );
+
+/*
+ * read/write traces. Does not manipulate the buffers at all, i.e. in order to
+ * make sense of the read trace it must be converted to native floats, and the
+ * buffer sent to write must be converted to target float.
+ */
+int segy_readtrace( FILE*,
+ unsigned int traceno,
+ float* buf,
+ long trace0,
+ unsigned int trace_bsize );
+
+int segy_writetrace( FILE*,
+ unsigned int traceno,
+ const float* buf,
+ long trace0,
+ unsigned int trace_bsize );
+
+/* convert to/from native float from segy formats (likely IBM or IEEE) */
+int segy_to_native( int format,
+ unsigned int size,
+ float* buf );
+
+int segy_from_native( int format,
+ unsigned int size,
+ float* buf );
+
+int segy_read_line( FILE* fp,
+ unsigned int line_trace0,
+ unsigned int line_length,
+ unsigned int stride,
+ float* buf,
+ long trace0,
+ unsigned int trace_bsize );
+
+int segy_write_line( FILE* fp,
+ unsigned int line_trace0,
+ unsigned int line_length,
+ unsigned int stride,
+ float* buf,
+ long trace0,
+ unsigned int trace_bsize );
+
+/*
+ * Count inlines and crosslines. Use this function to determine how large buffer
+ * the functions `segy_inline_indices` and `segy_crossline_indices` expect. If
+ * the file is sorted on inlines, `field` should the trace header field for the
+ * crossline number, and the inline number if the file is sorted on crosslines.
+ * If the file is sorted on inlines, `l1out` will contain the number of
+ * inlines, and `l2out` crosslines, and the other way around if the file is
+ * sorted on crosslines.
+ *
+ * `offsets` is the number of offsets in the file and be found with
+ * `segy_offsets`.
+ */
+int segy_count_lines( FILE*,
+ int field,
+ unsigned int offsets,
+ unsigned int* l1out,
+ unsigned int* l2out,
+ long trace0,
+ unsigned int trace_bsize );
+/*
+ * Find the `line_length` for the inlines. Assumes all inlines, crosslines and
+ * traces don't vary in length. `offsets` can be found with `segy_offsets`, and
+ * `traces` can be found with `segy_traces`.
+ *
+ * `inline_count` and `crossline_count` are the two values obtained with
+ * `segy_count_lines`.
+ */
+int segy_inline_length( int sorting,
+ unsigned int traces,
+ unsigned int crossline_count,
+ unsigned int offsets,
+ unsigned int* line_length );
+
+int segy_crossline_length( int sorting,
+ unsigned int traces,
+ unsigned int inline_count,
+ unsigned int offsets,
+ unsigned int* line_length );
+
+/*
+ * Find the indices of the inlines and write to `buf`. `offsets` are the number
+ * of offsets for this file as returned by `segy_offsets`
+ */
+int segy_inline_indices( FILE*,
+ int il,
+ int sorting,
+ unsigned int inline_count,
+ unsigned int crossline_count,
+ unsigned int offsets,
+ unsigned int* buf,
+ long trace0,
+ unsigned int trace_bsize );
+
+int segy_crossline_indices( FILE*,
+ int xl,
+ int sorting,
+ unsigned int inline_count,
+ unsigned int crossline_count,
+ unsigned int offsets,
+ unsigned int* buf,
+ long trace0,
+ unsigned int trace_bsize );
+
+/*
+ * Find the first `traceno` of the line `lineno`. `linenos` should be the line
+ * indices returned by `segy_inline_indices` or `segy_crossline_indices`. The
+ * stride depends on the sorting and is given by `segy_inline_stride` or
+ * `segy_crossline_stride`. `line_length` is the length, i.e. traces per line,
+ * given by `segy_inline_length` or `segy_crossline_length`.
+ *
+ * To read/write an inline, read `line_length` starting at `traceno`,
+ * incrementing `traceno` with `stride` `line_length` times.
+ */
+int segy_line_trace0( unsigned int lineno,
+ unsigned int line_length,
+ unsigned int stride,
+ const unsigned int* linenos,
+ const unsigned int linenos_sz,
+ unsigned int* traceno );
+
+/*
+ * Find the stride needed for an inline/crossline traversal.
+ */
+int segy_inline_stride( int sorting,
+ unsigned int inline_count,
+ unsigned int* stride );
+
+int segy_crossline_stride( int sorting,
+ unsigned int crossline_count,
+ unsigned int* stride );
+
+typedef enum {
+ TRACE_SEQUENCE_LINE = 1,
+ TRACE_SEQUENCE_FILE = 5,
+ FieldRecord = 9,
+ TraceNumber = 13,
+ EnergySourcePoint = 17,
+ CDP = 21,
+ CDP_TRACE = 25,
+ TraceIdentificationCode = 29,
+ NSummedTraces = 31,
+ NStackedTraces = 33,
+ DataUse = 35,
+ offset = 37,
+ ReceiverGroupElevation = 41,
+ SourceSurfaceElevation = 45,
+ SourceDepth = 49,
+ ReceiverDatumElevation = 53,
+ SourceDatumElevation = 57,
+ SourceWaterDepth = 61,
+ GroupWaterDepth = 65,
+ ElevationScalar = 69,
+ SourceGroupScalar = 71,
+ SourceX = 73,
+ SourceY = 77,
+ GroupX = 81,
+ GroupY = 85,
+ CoordinateUnits = 89,
+ WeatheringVelocity = 91,
+ SubWeatheringVelocity = 93,
+ SourceUpholeTime = 95,
+ GroupUpholeTime = 97,
+ SourceStaticCorrection = 99,
+ GroupStaticCorrection = 101,
+ TotalStaticApplied = 103,
+ LagTimeA = 105,
+ LagTimeB = 107,
+ DelayRecordingTime = 109,
+ MuteTimeStart = 111,
+ MuteTimeEND = 113,
+ TRACE_SAMPLE_COUNT = 115,
+ TRACE_SAMPLE_INTERVAL = 117,
+ GainType = 119,
+ InstrumentGainConstant = 121,
+ InstrumentInitialGain = 123,
+ Correlated = 125,
+ SweepFrequencyStart = 127,
+ SweepFrequencyEnd = 129,
+ SweepLength = 131,
+ SweepType = 133,
+ SweepTraceTaperLengthStart = 135,
+ SweepTraceTaperLengthEnd = 137,
+ TaperType = 139,
+ AliasFilterFrequency = 141,
+ AliasFilterSlope = 143,
+ NotchFilterFrequency = 145,
+ NotchFilterSlope = 147,
+ LowCutFrequency = 149,
+ HighCutFrequency = 151,
+ LowCutSlope = 153,
+ HighCutSlope = 155,
+ YearDataRecorded = 157,
+ DayOfYear = 159,
+ HourOfDay = 161,
+ MinuteOfHour = 163,
+ SecondOfMinute = 165,
+ TimeBaseCode = 167,
+ TraceWeightingFactor = 169,
+ GeophoneGroupNumberRoll1 = 171,
+ GeophoneGroupNumberFirstTraceOrigField = 173,
+ GeophoneGroupNumberLastTraceOrigField = 175,
+ GapSize = 177,
+ OverTravel = 179,
+ CDP_X = 181,
+ CDP_Y = 185,
+ INLINE_3D = 189,
+ CROSSLINE_3D = 193,
+ ShotPoint = 197,
+ ShotPointScalar = 201,
+ TraceValueMeasurementUnit = 203,
+ TransductionConstantMantissa = 205,
+ TransductionConstantPower = 209,
+ TransductionUnit = 211,
+ TraceIdentifier = 213,
+ ScalarTraceHeader = 215,
+ SourceType = 217,
+ SourceEnergyDirectionMantissa = 219,
+ SourceEnergyDirectionExponent = 223,
+ SourceMeasurementMantissa = 225,
+ SourceMeasurementExponent = 229,
+ SourceMeasurementUnit = 231,
+ UnassignedInt1 = 233,
+ UnassignedInt2 = 237
+
+} SEGY_FIELD;
+
+typedef enum {
+ BIN_JobID = 3201,
+ BIN_LineNumber = 3205,
+ BIN_ReelNumber = 3209,
+ BIN_Traces = 3213,
+ BIN_AuxTraces = 3215,
+ BIN_Interval = 3217,
+ BIN_IntervalOriginal = 3219,
+ BIN_Samples = 3221,
+ BIN_SamplesOriginal = 3223,
+ BIN_Format = 3225,
+ BIN_EnsembleFold = 3227,
+ BIN_SortingCode = 3229,
+ BIN_VerticalSum = 3231,
+ BIN_SweepFrequencyStart = 3233,
+ BIN_SweepFrequencyEnd = 3235,
+ BIN_SweepLength = 3237,
+ BIN_Sweep = 3239,
+ BIN_SweepChannel = 3241,
+ BIN_SweepTaperStart = 3243,
+ BIN_SweepTaperEnd = 3245,
+ BIN_Taper = 3247,
+ BIN_CorrelatedTraces = 3249,
+ BIN_BinaryGainRecovery = 3251,
+ BIN_AmplitudeRecovery = 3253,
+ BIN_MeasurementSystem = 3255,
+ BIN_ImpulseSignalPolarity = 3257,
+ BIN_VibratoryPolarity = 3259,
+ BIN_Unassigned1 = 3261,
+ BIN_SEGYRevision = 3501,
+ BIN_TraceFlag = 3503,
+ BIN_ExtendedHeaders = 3505,
+ BIN_Unassigned2 = 3507,
+} SEGY_BINFIELD;
+
+typedef enum {
+ IBM_FLOAT_4_BYTE = 1,
+ SIGNED_INTEGER_4_BYTE = 2,
+ SIGNED_SHORT_2_BYTE = 3,
+ FIXED_POINT_WITH_GAIN_4_BYTE = 4, // Obsolete
+ IEEE_FLOAT_4_BYTE = 5,
+ NOT_IN_USE_1 = 6,
+ NOT_IN_USE_2 = 7,
+ SIGNED_CHAR_1_BYTE = 8
+} SEGY_FORMAT;
+
+typedef enum {
+ UNKNOWN_SORTING = 0,
+ CROSSLINE_SORTING = 1,
+ INLINE_SORTING = 2,
+
+} SEGY_SORTING;
+
+typedef enum {
+ SEGY_OK = 0,
+ SEGY_FOPEN_ERROR,
+ SEGY_FSEEK_ERROR,
+ SEGY_FREAD_ERROR,
+ SEGY_FWRITE_ERROR,
+ SEGY_INVALID_FIELD,
+ SEGY_INVALID_SORTING,
+ SEGY_MISSING_LINE_INDEX,
+ SEGY_INVALID_OFFSETS,
+ SEGY_TRACE_SIZE_MISMATCH,
+} SEGY_ERROR;
+
+
+#endif //SEGYIO_SEGY_H
diff --git a/src/spec/segyspec.c b/src/spec/segyspec.c
new file mode 100644
index 0000000..a22b373
--- /dev/null
+++ b/src/spec/segyspec.c
@@ -0,0 +1,129 @@
+#include <malloc.h>
+
+#include "segyio/segy.h"
+#include "segyspec.h"
+
+static char* copyString(const char* path) {
+ size_t size = strlen(path) + 1;
+ char* path_copy = malloc(size);
+ memcpy(path_copy, path, size);
+ return path_copy;
+}
+
+
+int segyCreateSpec(SegySpec* spec, const char* file, unsigned int inline_field, unsigned int crossline_field, double t0) {
+
+ int errc = 0;
+
+ FILE* fp = fopen( file, "r" );
+ if (fp == NULL) {
+ fprintf(stderr, "Unable to open file: '%s'\n", file);
+ return -1;
+ }
+
+ spec->sample_indexes = NULL;
+ spec->inline_indexes = NULL;
+ spec->crossline_indexes = NULL;
+
+ char header[ SEGY_BINARY_HEADER_SIZE ];
+ errc = segy_binheader( fp, header );
+ if (errc!=0) {
+ goto CLEANUP;
+ }
+
+ spec->filename = copyString(file);
+ spec->sample_format = segy_format( header );
+ spec->sample_count = segy_samples( header );
+
+ spec->sample_indexes = malloc(sizeof(double) * spec->sample_count);
+ errc = segy_sample_indexes(fp, spec->sample_indexes, t0, spec->sample_count);
+ if (errc != 0) {
+ goto CLEANUP;
+ }
+
+ const long trace0 = segy_trace0( header );
+
+ spec->trace_bsize = segy_trace_bsize( segy_samples( header ) );
+ size_t traces;
+ errc = segy_traces(fp, &traces, trace0, spec->trace_bsize);
+ if (errc != 0) {
+ goto CLEANUP;
+ }
+
+ errc = segy_offsets(fp, inline_field, crossline_field, traces, &spec->offset_count, trace0, spec->trace_bsize);
+ if (errc != 0) {
+ goto CLEANUP;
+ }
+
+ errc = segy_sorting(fp, inline_field, crossline_field, &spec->trace_sorting_format, trace0, spec->trace_bsize);
+ if (errc != 0) {
+ goto CLEANUP;
+ }
+
+ unsigned int* l1;
+ unsigned int* l2;
+ unsigned int field;
+ if (spec->trace_sorting_format == INLINE_SORTING) {
+ field = crossline_field;
+ l1 = &spec->inline_count;
+ l2 = &spec->crossline_count;
+ } else if (spec->trace_sorting_format == CROSSLINE_SORTING) {
+ field = inline_field;
+ l2 = &spec->inline_count;
+ l1 = &spec->crossline_count;
+ } else {
+ fprintf(stderr, "Unknown sorting\n");
+ goto CLEANUP;
+ }
+
+ errc = segy_count_lines(fp, field, spec->offset_count, l1, l2, trace0, spec->trace_bsize);
+ if (errc != 0) {
+ goto CLEANUP;
+ }
+
+ spec->inline_indexes = malloc(sizeof(unsigned int) * spec->inline_count);
+ spec->crossline_indexes = malloc(sizeof(unsigned int) * spec->crossline_count);
+
+ errc = segy_inline_indices(fp, inline_field, spec->trace_sorting_format,
+ spec->inline_count, spec->crossline_count, spec->offset_count, spec->inline_indexes,
+ trace0, spec->trace_bsize);
+ if (errc != 0) {
+ goto CLEANUP;
+ }
+
+ errc = segy_crossline_indices(fp, crossline_field, spec->trace_sorting_format,
+ spec->inline_count, spec->crossline_count, spec->offset_count, spec->crossline_indexes,
+ trace0, spec->trace_bsize);
+ if (errc != 0) {
+ goto CLEANUP;
+ }
+
+ spec->first_trace_pos = segy_trace0( header );
+
+ errc = segy_inline_stride(spec->trace_sorting_format, spec->inline_count, &spec->il_stride);
+ if (errc != 0) {
+ goto CLEANUP;
+ }
+
+ errc = segy_crossline_stride(spec->trace_sorting_format, spec->crossline_count, &spec->xl_stride);
+ if (errc != 0) {
+ goto CLEANUP;
+ }
+
+ fclose(fp);
+
+ return 0;
+
+ CLEANUP:
+ if (spec->crossline_indexes != NULL)
+ free(spec->crossline_indexes);
+ if (spec->inline_indexes != NULL)
+ free(spec->inline_indexes);
+ if (spec->sample_indexes != NULL)
+ free(spec->sample_indexes);
+ free(spec->filename);
+
+ fclose(fp);
+
+ return errc;
+}
diff --git a/src/spec/segyspec.h b/src/spec/segyspec.h
new file mode 100644
index 0000000..80241f8
--- /dev/null
+++ b/src/spec/segyspec.h
@@ -0,0 +1,33 @@
+#ifndef SEGYIO_SEGYSPEC_H
+#define SEGYIO_SEGYSPEC_H
+
+#include <string.h>
+
+typedef struct {
+ char* filename;
+
+ unsigned int sample_format;
+
+ unsigned int* crossline_indexes;
+ unsigned int crossline_count;
+
+ unsigned int* inline_indexes;
+ unsigned int inline_count;
+
+ unsigned int offset_count;
+
+ double* sample_indexes;
+ unsigned int sample_count;
+
+ unsigned int trace_sorting_format;
+
+ unsigned int il_stride;
+ unsigned int xl_stride;
+ unsigned int first_trace_pos;
+ unsigned int trace_bsize;
+
+} SegySpec;
+
+int segyCreateSpec(SegySpec* spec, const char* file, unsigned int inline_field, unsigned int crossline_field, double t0);
+
+#endif //SEGYIO_SEGYSPEC_H
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
new file mode 100644
index 0000000..0a64837
--- /dev/null
+++ b/tests/CMakeLists.txt
@@ -0,0 +1,14 @@
+configure_file(test-data/small.sgy test-data/small.sgy COPYONLY)
+configure_file(test-data/text.sgy test-data/text.sgy COPYONLY)
+configure_file(test-data/small.sgy test-data/small-traceheader.sgy COPYONLY)
+
+add_segyio_test(utils test_utils.c)
+add_segyio_test(segy test_segy.c)
+add_segyio_test(segyspec test_segyspec.c)
+
+if(BUILD_MEX)
+ add_matlab_test(matlab.segyspec test_segyspec_mex.m test_segyspec_mex)
+ add_matlab_test(matlab.segy test_segy_mex.m test_segy_mex)
+endif()
+
+add_python_test(python.segy test_segy.py)
diff --git a/tests/test-data/small.sgy b/tests/test-data/small.sgy
new file mode 100644
index 0000000..bb659b6
Binary files /dev/null and b/tests/test-data/small.sgy differ
diff --git a/tests/test-data/text.sgy b/tests/test-data/text.sgy
new file mode 100644
index 0000000..d9e2926
Binary files /dev/null and b/tests/test-data/text.sgy differ
diff --git a/tests/test_segy.c b/tests/test_segy.c
new file mode 100644
index 0000000..23d1f1f
--- /dev/null
+++ b/tests/test_segy.c
@@ -0,0 +1,609 @@
+#include <math.h>
+#include <stdlib.h>
+
+#include <segyio/segy.h>
+
+#include "unittest.h"
+
+void test_interpret_file() {
+ /*
+ * test all structural properties of a file, i.e. traces, samples, sorting
+ * etc, but don't actually read any data
+ */
+ const char *file = "test-data/small.sgy";
+
+ int err;
+ char header[ SEGY_BINARY_HEADER_SIZE ];
+ int sorting;
+ size_t traces;
+ unsigned int inlines_sz, crosslines_sz;
+ unsigned int offsets, stride;
+ unsigned int line_trace0, line_length;
+ const int il = INLINE_3D;
+ const int xl = CROSSLINE_3D;
+
+ FILE* fp = fopen( file, "r" );
+
+ assertTrue( fp != NULL, "Could not open file." );
+ err = segy_binheader( fp, header );
+ assertTrue( err == 0, "Could not read binary header." );
+
+ const long trace0 = segy_trace0( header );
+ assertTrue( trace0 == 3600,
+ "Wrong byte offset of the first trace header. Expected 3600." );
+
+ const unsigned int samples = segy_samples( header );
+ assertTrue( samples == 50, "Expected 350 samples per trace." );
+
+ const size_t trace_bsize = segy_trace_bsize( samples );
+ assertTrue( trace_bsize == 50 * 4,
+ "Wrong trace byte size. Expected samples * 4-byte float." );
+
+ err = segy_traces( fp, &traces, trace0, trace_bsize );
+ assertTrue( err == 0, "Could not determine number of traces in this file." );
+ assertTrue( traces == 25, "Expected 25 traces in the file." );
+
+ err = segy_sorting( fp, xl, il, &sorting, trace0, trace_bsize );
+ assertTrue( err == 0, "Could not figure out file sorting." );
+ assertTrue( sorting == CROSSLINE_SORTING, "Expected crossline sorting." );
+
+ err = segy_sorting( fp, il, xl, &sorting, trace0, trace_bsize );
+ assertTrue( err == 0, "Could not figure out file sorting." );
+ assertTrue( sorting == INLINE_SORTING, "Expected inline sorting." );
+
+ err = segy_offsets( fp, il, xl, traces, &offsets, trace0, trace_bsize );
+ assertTrue( err == 0, "Could not figure out offsets." );
+ assertTrue( offsets == 1, "Expected offsets to be 1 (no extra offsets)." );
+
+ err = segy_count_lines( fp, xl, offsets, &inlines_sz, &crosslines_sz, trace0, trace_bsize );
+ assertTrue( err == 0, "Could not determine line count in this file." );
+ assertTrue( inlines_sz == 5, "Expected 5 inlines." );
+ assertTrue( crosslines_sz == 5, "Expected 5 crosslines." );
+
+ /* Inline-specific information */
+ unsigned int inline_indices[ 5 ];
+ err = segy_inline_indices( fp, il, sorting, inlines_sz, crosslines_sz, offsets, inline_indices, trace0, trace_bsize );
+ assertTrue( err == 0, "Could not determine inline linenos." );
+ for( unsigned int i = 0, ref = 1; i < 5; ++i, ++ref )
+ assertTrue( inline_indices[ i ] == ref,
+ "Inline lineno mismatch, should be [1..5]." );
+
+ err = segy_inline_stride( sorting, inlines_sz, &stride );
+ assertTrue( err == 0, "Failure while reading stride." );
+ assertTrue( stride == 1, "Expected inline stride = 1." );
+
+ err = segy_line_trace0( 4, crosslines_sz, stride, inline_indices, inlines_sz, &line_trace0 );
+ assertTrue( err == 0, "Could not determine 2484's trace0." );
+ assertTrue( line_trace0 == 15, "Line 4 should start at traceno 15." );
+
+ err = segy_inline_length( sorting, traces, inlines_sz, offsets, &line_length );
+ assertTrue( err == 0, "Could not determine line length." );
+ assertTrue( line_length == 5, "Inline length should be 5." );
+
+ /* Crossline-specific information */
+ unsigned int crossline_indices[ 5 ];
+ err = segy_crossline_indices( fp, xl, sorting, inlines_sz, crosslines_sz, offsets, crossline_indices, trace0, trace_bsize );
+ assertTrue( err == 0, "Could not determine crossline linenos." );
+ for( unsigned int i = 0, ref = 20; i < 5; ++i, ++ref )
+ assertTrue( crossline_indices[ i ] == ref,
+ "Crossline lineno mismatch, should be [20..24]." );
+
+ err = segy_crossline_stride( sorting, crosslines_sz, &stride );
+ assertTrue( err == 0, "Failure while reading stride." );
+ assertTrue( stride == 5, "Expected crossline stride = 5." );
+
+ err = segy_line_trace0( 22, crosslines_sz, stride, crossline_indices, inlines_sz, &line_trace0 );
+ assertTrue( err == 0, "Could not determine 22's trace0." );
+ assertTrue( line_trace0 == 2, "Line 22 should start at traceno 2." );
+
+ err = segy_crossline_length( sorting, traces, inlines_sz, offsets, &line_length );
+ assertTrue( err == 0, "Failure finding length" );
+ assertTrue( line_length == 5, "Crossline length should be 5." );
+
+ fclose( fp );
+}
+
+/* Prestack test for when we have an approperiate prestack file
+
+void test_interpret_file_prestack() {
+ const char *file = "test-data/prestack.sgy";
+
+ int err;
+ char header[ SEGY_BINARY_HEADER_SIZE ];
+ int sorting;
+ size_t traces;
+ unsigned int inlines_sz, crosslines_sz;
+ unsigned int offsets, stride;
+ unsigned int line_trace0, line_length;
+ const int il = INLINE_3D;
+ const int xl = CROSSLINE_3D;
+
+ FILE* fp = fopen( file, "r" );
+
+ assertTrue( fp != NULL, "Could not open file." );
+ err = segy_binheader( fp, header );
+ assertTrue( err == 0, "Could not read binary header." );
+
+ const long trace0 = segy_trace0( header );
+ assertTrue( trace0 == 3600,
+ "Wrong byte offset of the first trace header. Expected 3600." );
+
+ const unsigned int samples = segy_samples( header );
+ assertTrue( samples == 326, "Expected 350 samples per trace." );
+
+ const size_t trace_bsize = segy_trace_bsize( samples );
+ assertTrue( trace_bsize == 326 * 4,
+ "Wrong trace byte size. Expected samples * 4-byte float." );
+
+ err = segy_traces( fp, &traces, trace0, trace_bsize );
+ assertTrue( err == 0, "Could not determine number of traces in this file." );
+ assertTrue( traces == 16926, "Expected 286 traces in the file." );
+
+ err = segy_sorting( fp, xl, il, &sorting, trace0, trace_bsize );
+ assertTrue( err == 0, "Could not figure out file sorting." );
+ assertTrue( sorting == CROSSLINE_SORTING, "Expected crossline sorting." );
+
+ err = segy_sorting( fp, il, xl, &sorting, trace0, trace_bsize );
+ assertTrue( err == 0, "Could not figure out file sorting." );
+ assertTrue( sorting == INLINE_SORTING, "Expected inline sorting." );
+
+ err = segy_offsets( fp, il, xl, traces, &offsets, trace0, trace_bsize );
+ assertTrue( err == 0, "Could not figure out offsets." );
+ assertTrue( offsets == 26, "Expected offsets to be 26." );
+
+ err = segy_count_lines( fp, xl, offsets, &inlines_sz, &crosslines_sz, trace0, trace_bsize );
+ assertTrue( err == 0, "Could not determine line count in this file." );
+ assertTrue( inlines_sz == 31, "Expected 22 inlines." );
+ assertTrue( crosslines_sz == 21, "Expected 13 crosslines." );
+
+ unsigned int inline_indices[ 31 ];
+ err = segy_inline_indices( fp, il, sorting, inlines_sz, crosslines_sz, offsets, inline_indices, trace0, trace_bsize );
+ assertTrue( err == 0, "Could not determine inline linenos." );
+ for( unsigned int i = 0, ref = 2500; i < 31; ++i, ++ref )
+ assertTrue( inline_indices[ i ] == ref,
+ "Inline lineno mismatch, should be [2500..2530]." );
+
+ err = segy_inline_stride( sorting, inlines_sz, &stride );
+ assertTrue( err == 0, "Failure while reading stride." );
+ assertTrue( stride == 1, "Expected inline stride = 1." );
+
+ err = segy_line_trace0( 2504, crosslines_sz, stride, inline_indices, inlines_sz, &line_trace0 );
+ assertTrue( err == 0, "Could not determine 2504's trace0." );
+ assertTrue( line_trace0 == 84, "Line 2504 should start at traceno 84." );
+
+ err = segy_inline_length( sorting, traces, inlines_sz, offsets, &line_length );
+ assertTrue( err == 0, "Could not determine line length." );
+ assertTrue( line_length == 31, "Inline length should be 31." );
+
+ unsigned int crossline_indices[ 21 ];
+ err = segy_crossline_indices( fp, xl, sorting, inlines_sz, crosslines_sz, offsets, crossline_indices, trace0, trace_bsize );
+ assertTrue( err == 0, "Could not determine crossline linenos." );
+ for( unsigned int i = 0, ref = 2220; i < 21; ++i, ++ref )
+ assertTrue( crossline_indices[ i ] == ref,
+ "Crossline lineno mismatch, should be [2220..2240]." );
+
+ err = segy_crossline_stride( sorting, crosslines_sz, &stride );
+ assertTrue( err == 0, "Failure while reading stride." );
+ assertTrue( stride == 21, "Expected crossline stride = 13." );
+
+ err = segy_line_trace0( 2222, crosslines_sz, stride, crossline_indices, inlines_sz, &line_trace0 );
+ assertTrue( err == 0, "Could not determine 2222's trace0." );
+ assertTrue( line_trace0 == 2, "Line 2222 should start at traceno 5." );
+
+ err = segy_crossline_length( sorting, traces, inlines_sz, offsets, &line_length );
+ assertTrue( err == 0, "Failure finding length" );
+ assertTrue( line_length == 31, "Crossline length should be 31." );
+
+ fclose( fp );
+}
+
+*/
+
+void testReadInLine_4(){
+ const char *file = "test-data/small.sgy";
+
+ int sorting;
+ size_t traces;
+ unsigned int inlines_sz, crosslines_sz;
+ unsigned int offsets, stride;
+ unsigned int line_trace0, line_length;
+
+ /* test specific consts */
+ const int il = INLINE_3D, xl = CROSSLINE_3D;
+
+ char header[ SEGY_BINARY_HEADER_SIZE ];
+
+ FILE* fp = fopen( file, "r" );
+ assertTrue( 0 == segy_binheader( fp, header ), "Could not read header" );
+ const long trace0 = segy_trace0( header );
+ const unsigned int samples = segy_samples( header );
+ const size_t trace_bsize = segy_trace_bsize( samples );
+ const int format = segy_format( header );
+
+ unsigned int inline_indices[ 5 ];
+ const unsigned int inline_length = 5;
+ float* data = malloc( inline_length * samples * sizeof( float ) );
+
+ int ok = 0;
+ ok = !segy_traces( fp, &traces, trace0, trace_bsize )
+ && !segy_sorting( fp, il, xl, &sorting, trace0, trace_bsize )
+ && !segy_offsets( fp, il, xl, traces, &offsets, trace0, trace_bsize )
+ && !segy_count_lines( fp, xl, offsets, &inlines_sz, &crosslines_sz, trace0, trace_bsize )
+ && !segy_inline_indices( fp, il, sorting, inlines_sz, crosslines_sz, offsets, inline_indices, trace0, trace_bsize )
+ && !segy_inline_stride( sorting, inlines_sz, &stride )
+ && !segy_line_trace0( 4, crosslines_sz, stride, inline_indices, inlines_sz, &line_trace0 )
+ && !segy_inline_length( sorting, traces, inlines_sz, offsets, &line_length )
+ && !segy_read_line( fp, line_trace0, line_length, stride, data, trace0, trace_bsize )
+ && !segy_to_native( format, inline_length * samples, data );
+
+ assertTrue( ok, "Error in setup. "
+ "This integrity should be covered by other tests." );
+
+ //first xline
+ //first sample
+ assertClose(4.2f, data[0], 0.0001f);
+ //middle sample
+ assertClose(4.20024f, data[samples/2-1], 0.0001f);
+ //last sample
+ assertClose(4.20049f, data[samples-1], 0.0001f);
+
+ //middle xline
+ size_t middle_line = 2;
+ //first sample
+ assertClose(4.22f, data[samples*middle_line+0], 0.0001);
+ //middle sample
+ assertClose(4.22024f, data[samples*middle_line+samples/2-1], 0.0001);
+ //last sample
+ assertClose(4.22049f, data[samples*middle_line+samples-1], 0.0001);
+
+ //last xline
+ size_t last_line = (crosslines_sz-1);
+ //first sample
+ assertClose(4.24f, data[samples*last_line+0], 0);
+ //middle sample
+ assertClose(4.24024f, data[samples*last_line+samples/2-1], 0.0001);
+ //last sample
+ assertClose(4.24049f, data[samples*last_line+samples-1], 0.0001);
+
+ for( float* ptr = data; ptr < data + (samples * line_length); ++ptr )
+ assertTrue( *ptr >= 4.0f && *ptr <= 5.0f, "Sample value not in range." );
+
+ free(data);
+ fclose(fp);
+}
+
+void testReadCrossLine_22(){
+ const char *file = "test-data/small.sgy";
+
+ int sorting;
+ size_t traces;
+ unsigned int inlines_sz, crosslines_sz;
+ unsigned int offsets, stride;
+ unsigned int line_trace0, line_length;
+
+ /* test specific consts */
+ const int il = INLINE_3D, xl = CROSSLINE_3D;
+
+ char header[ SEGY_BINARY_HEADER_SIZE ];
+
+ FILE* fp = fopen( file, "r" );
+ assertTrue( 0 == segy_binheader( fp, header ), "Could not read header" );
+ const long trace0 = segy_trace0( header );
+ const unsigned int samples = segy_samples( header );
+ const size_t trace_bsize = segy_trace_bsize( samples );
+ const int format = segy_format( header );
+
+ unsigned int crossline_indices[ 5 ];
+ const unsigned int crossline_length = 5;
+ float* data = malloc( crossline_length * samples * sizeof(float) );
+
+ int ok = 0;
+ ok = !segy_traces( fp, &traces, trace0, trace_bsize )
+ && !segy_sorting( fp, il, xl, &sorting, trace0, trace_bsize )
+ && !segy_offsets( fp, il, xl, traces, &offsets, trace0, trace_bsize )
+ && !segy_count_lines( fp, xl, offsets, &inlines_sz, &crosslines_sz, trace0, trace_bsize )
+ && !segy_crossline_indices( fp, xl, sorting, inlines_sz, crosslines_sz, offsets, crossline_indices, trace0, trace_bsize )
+ && !segy_crossline_stride( sorting, crosslines_sz, &stride )
+ && !segy_line_trace0( 22, crosslines_sz, stride, crossline_indices, inlines_sz, &line_trace0 )
+ && !segy_crossline_length( sorting, traces, inlines_sz, offsets, &line_length )
+ && !segy_read_line( fp, line_trace0, line_length, stride, data, trace0, trace_bsize )
+ && !segy_to_native( format, crossline_length * samples, data );
+
+ assertTrue( ok, "Error in setup. "
+ "This integrity should be covered by other tests." );
+
+ //first inline
+ //first sample
+ assertClose(1.22f, data[0], 0.0001);
+ //middle sample
+ assertClose(1.22024f, data[samples/2-1], 0.0001);
+ //last sample
+ assertClose(1.22049f, data[samples-1], 0.0001);
+
+ //middle inline
+ size_t middle_line = 2;
+ //first sample
+ assertClose(3.22f, data[samples*middle_line+0], 0.0001);
+ //middle sample
+ assertClose(3.22024f, data[samples*middle_line+samples/2-1], 0.0001);
+ //last sample
+ assertClose(3.22049f, data[samples*middle_line+samples-1], 0.0001);
+
+ //last inline
+ size_t last_line = (line_length-1);
+ //first sample
+ assertClose(5.22f, data[samples*last_line+0], 0.0001);
+ //middle sample
+ assertClose(5.22024f, data[samples*last_line+samples/2-1], 0.0001);
+ //last sample
+ assertClose(5.22049f, data[samples*last_line+samples-1], 0.0001);
+
+ for( float* ptr = data; ptr < data + (samples * line_length); ++ptr ) {
+ float xl = *ptr - floorf(*ptr);
+ assertTrue( 0.219f <= xl && xl <= 0.231f, "Sample value not in range" );
+ }
+
+ free(data);
+ fclose(fp);
+}
+
+int32_t from_int32( int32_t );
+
+void test_modify_trace_header() {
+ const char *file = "test-data/small-traceheader.sgy";
+
+ int err;
+ char bheader[ SEGY_BINARY_HEADER_SIZE ];
+
+ FILE* fp = fopen( file, "r+" );
+ err = segy_binheader( fp, bheader );
+ assertTrue( err == 0, "Could not read header" );
+ const long trace0 = segy_trace0( bheader );
+ const unsigned int samples = segy_samples( bheader );
+ const size_t trace_bsize = segy_trace_bsize( samples );
+
+ char traceh[ SEGY_TRACE_HEADER_SIZE ];
+ err = segy_traceheader( fp, 0, traceh, trace0, trace_bsize );
+ assertTrue( err == 0, "Error while reading trace header." );
+
+ int ilno;
+ err = segy_get_field( traceh, INLINE_3D, &ilno );
+ assertTrue( err == 0, "Error while reading iline no from field." );
+ assertTrue( ilno == 1, "Wrong iline no." );
+
+ err = segy_set_field( traceh, INLINE_3D, ilno + 1 );
+ assertTrue( err == 0, "Error writing to trace header buffer." );
+
+ err = segy_get_field( traceh, INLINE_3D, &ilno );
+ assertTrue( err == 0, "Error while reading iline no from dirty field." );
+ assertTrue( ilno == 2, "Wrong iline no." );
+
+ err = segy_write_traceheader( fp, 0, traceh, trace0, trace_bsize );
+ fflush( fp );
+
+ err = segy_traceheader( fp, 0, traceh, trace0, trace_bsize );
+ assertTrue( err == 0, "Error while reading trace header." );
+ err = segy_get_field( traceh, INLINE_3D, &ilno );
+ assertTrue( err == 0, "Error while reading iline no from dirty field." );
+ assertTrue( ilno == 2, "Wrong iline no." );
+
+ err = segy_set_field( traceh, INLINE_3D, 1 );
+ assertTrue( err == 0, "Error writing to trace header buffer." );
+ err = segy_write_traceheader( fp, 0, traceh, trace0, trace_bsize );
+ assertTrue( err == 0, "Error writing traceheader." );
+
+ fclose( fp );
+}
+
+static const char* expected_textheader =
+ "C 1 DATE: 22/02/2016 "
+ "C 2 AN INCREASE IN AMPLITUDE EQUALS AN INCREASE IN ACOUSTIC IMPEDANCE "
+ "C 3 FIRST SAMPLE: 4 MS, LAST SAMPLE: 1400 MS, SAMPLE INTERVAL: 4 MS "
+ "C 4 DATA RANGE: INLINES=(2479-2500) (INC 1),CROSSLINES=(1428-1440) (INC 1) "
+ "C 5 PROCESSING GRID CORNERS: "
+ "C 6 DISTANCE BETWEEN INLINES: 2499.75 M, CROSSLINES: 1250 M "
+ "C 7 1: INLINE 2479, CROSSLINE 1428, UTM-X 9976386.00, UTM-Y 9989096.00 "
+ "C 8 2: INLINE 2479, CROSSLINE 1440, UTM-X 9983886.00, UTM-Y 10002087.00 "
+ "C 9 3: INLINE 2500, CROSSLINE 1428, UTM-X 10021847.00, UTM-Y 9962849.00 "
+ "C10 4: INLINE 2500, CROSSLINE 1440, UTM-X 10029348.00, UTM-Y 9975839.00 "
+ "C11 TRACE HEADER POSITION: "
+ "C12 INLINE BYTES 005-008 | OFFSET BYTES 037-040 "
+ "C13 CROSSLINE BYTES 021-024 | CMP UTM-X BYTES 181-184 "
+ "C14 CMP UTM-Y BYTES 185-188 "
+ "C15 END EBCDIC HEADER "
+ "C16 "
+ "C17 "
+ "C18 "
+ "C19 "
+ "C20 "
+ "C21 "
+ "C22 "
+ "C23 "
+ "C24 "
+ "C25 "
+ "C26 "
+ "C27 "
+ "C28 "
+ "C29 "
+ "C30 "
+ "C31 "
+ "C32 "
+ "C33 "
+ "C34 "
+ "C35 "
+ "C36 "
+ "C37 "
+ "C38 "
+ "C39 "
+ "C40 \x80";
+
+void test_text_header() {
+ const char *file = "test-data/text.sgy";
+ FILE* fp = fopen( file, "r" );
+
+ char ascii[ SEGY_TEXT_HEADER_SIZE + 1 ] = { 0 };
+ int err = segy_textheader( fp, ascii );
+ assertTrue( err == 0, "Could not read text header" );
+ assertTrue( strcmp(expected_textheader, ascii) == 0, "Text headers did not match" );
+
+ fclose( fp );
+}
+
+void test_trace_header_errors() {
+ const char *file = "test-data/small.sgy";
+ FILE* fp = fopen( file, "r" );
+ int err;
+
+ char binheader[ SEGY_BINARY_HEADER_SIZE ];
+ err = segy_binheader( fp, binheader );
+ assertTrue( err == 0, "Could not read binary header." );
+ const unsigned samples = segy_samples( binheader );
+ const unsigned bsize = segy_trace_bsize( samples );
+ const long trace0 = segy_trace0( binheader );
+
+ char header[ SEGY_TRACE_HEADER_SIZE ];
+
+ err = segy_traceheader( fp, 0, header, trace0, bsize );
+ assertTrue( err == 0, "Could not read trace header." );
+
+ /* reading outside header should yield invalid field */
+ int field;
+ err = segy_get_field( header, SEGY_TRACE_HEADER_SIZE + 10, &field );
+ assertTrue( err == SEGY_INVALID_FIELD, "Reading outside trace header." );
+
+ /* Reading between known byte offsets should yield error */
+ err = segy_get_field( header, INLINE_3D + 1, &field );
+ assertTrue( err == SEGY_INVALID_FIELD, "Reading between ok byte offsets." );
+
+ err = segy_get_field( header, INLINE_3D, &field );
+ assertTrue( err == SEGY_OK, "Reading failed at valid byte offset." );
+
+ err = segy_get_field( header, DayOfYear, &field );
+ assertTrue( err == SEGY_OK, "Reading failed at valid byte offset." );
+
+ fclose( fp );
+}
+
+void test_file_error_codes() {
+ const char *file = "test-data/small.sgy";
+ FILE* fp = fopen( file, "r" );
+ fclose( fp );
+
+ int err;
+ char binheader[ SEGY_BINARY_HEADER_SIZE ];
+ err = segy_binheader( fp, binheader );
+ assertTrue( err == SEGY_FSEEK_ERROR,
+ "Could read binary header from invalid file." );
+
+ const unsigned samples = segy_samples( binheader );
+ const unsigned trace_bsize = segy_trace_bsize( samples );
+ const long trace0 = segy_trace0( binheader );
+
+ char header[ SEGY_TRACE_HEADER_SIZE ];
+
+ err = segy_traceheader( fp, 0, header, trace0, trace_bsize );
+ assertTrue( err == SEGY_FSEEK_ERROR, "Could not read trace header." );
+
+ /* reading outside header should yield invalid field */
+ int field;
+ err = segy_get_field( header, SEGY_TRACE_HEADER_SIZE + 10, &field );
+ assertTrue( err == SEGY_INVALID_FIELD, "Reading outside trace header." );
+
+ /* Reading between known byte offsets should yield error */
+ err = segy_get_field( header, INLINE_3D + 1, &field );
+ assertTrue( err == SEGY_INVALID_FIELD, "Reading between ok byte offsets." );
+
+ err = segy_get_field( header, INLINE_3D, &field );
+ assertTrue( err == SEGY_OK, "Reading failed at valid byte offset." );
+
+ err = segy_get_field( header, DayOfYear, &field );
+ assertTrue( err == SEGY_OK, "Reading failed at valid byte offset." );
+
+ err = segy_textheader( fp, NULL );
+ assertTrue( err == SEGY_FSEEK_ERROR, "Could seek in invalid file." );
+
+ size_t traces;
+ err = segy_traces( fp, &traces, 3600, 350 );
+ assertTrue( err == SEGY_FSEEK_ERROR, "Could seek in invalid file." );
+
+ int sorting;
+ err = segy_sorting( fp, 0, 0, &sorting, 3600, 350 );
+ assertTrue( err == SEGY_FSEEK_ERROR, "Could seek in invalid file." );
+
+ err = segy_readtrace( fp, 0, NULL, 3600, 350 );
+ assertTrue( err == SEGY_FSEEK_ERROR, "Could seek in invalid file." );
+
+ err = segy_writetrace( fp, 0, NULL, 3600, 350 );
+ assertTrue( err == SEGY_FSEEK_ERROR, "Could seek in invalid file." );
+
+ unsigned int l1, l2;
+ err = segy_count_lines( fp, 0, 1, &l1, &l2, 3600, 350 );
+ assertTrue( err == SEGY_FSEEK_ERROR, "Could seek in invalid file." );
+}
+
+void test_error_codes_sans_file() {
+ int err;
+
+ unsigned int linenos[] = { 0, 1, 2 };
+ err = segy_line_trace0( 10, 3, 1, linenos, 3, NULL );
+ assertTrue( err == SEGY_MISSING_LINE_INDEX,
+ "Found line number that shouldn't exist." );
+
+ unsigned int stride;
+ err = segy_inline_stride( INLINE_SORTING + 3, 10, &stride );
+ assertTrue( err == SEGY_INVALID_SORTING,
+ "Expected sorting to be invalid." );
+
+ err = segy_crossline_stride( INLINE_SORTING + 3, 10, &stride );
+ assertTrue( err == SEGY_INVALID_SORTING,
+ "Expected sorting to be invalid." );
+
+ err = segy_inline_length( INLINE_SORTING + 3, 10, 10, 10, NULL );
+ assertTrue( err == SEGY_INVALID_SORTING,
+ "Expected sorting to be invalid." );
+
+ err = segy_crossline_length( INLINE_SORTING + 3, 10, 10, 10, NULL );
+ assertTrue( err == SEGY_INVALID_SORTING,
+ "Expected sorting to be invalid." );
+
+}
+
+
+/*
+ * segy_seek is private to the implementation, but we need external linkage for
+ * this test.
+ */
+int segy_seek( FILE*, unsigned int, long, unsigned int );
+
+void test_file_size_above_4GB(){
+ FILE* fp = tmpfile();
+
+ unsigned int trace = 5e6;
+ unsigned int trace_bsize = 1e3;
+ long trace0 = 0;
+ int err = segy_seek( fp, trace, trace0, trace_bsize);
+ assertTrue(err==0, "");
+ long pos = ftell(fp);
+ assertTrue(pos == (long)trace*((long)trace_bsize+SEGY_TRACE_HEADER_SIZE), "seek overflow");
+ fclose(fp);
+}
+
+int main() {
+ test_interpret_file();
+ /* test_interpret_file_prestack(); */
+ testReadInLine_4();
+ testReadCrossLine_22();
+ test_modify_trace_header();
+ test_text_header();
+ test_trace_header_errors();
+ /*
+ * due to its barely-defined behavorial nature, this test is commented out
+ * for most runs, as it would trip up the memcheck test
+ *
+ * test_file_error_codes();
+ */
+ test_error_codes_sans_file();
+
+ test_file_size_above_4GB();
+ exit(0);
+}
diff --git a/tests/test_segy.py b/tests/test_segy.py
new file mode 100644
index 0000000..96b2cb0
--- /dev/null
+++ b/tests/test_segy.py
@@ -0,0 +1,469 @@
+import numpy as np
+from unittest import TestCase
+import segyio
+from segyio import TraceField, BinField
+import shutil
+import sys
+import time
+import filecmp
+
+class TestSegy(TestCase):
+
+ def setUp(self):
+ self.filename = "test-data/small.sgy"
+
+ def test_inline_4(self):
+ with segyio.open(self.filename, "r") as f:
+
+ sample_count = f.samples
+ self.assertEqual(50, sample_count)
+
+ data = f.iline[4]
+
+ self.assertAlmostEqual(4.2, data[0, 0], places = 6)
+ # middle sample
+ self.assertAlmostEqual(4.20024, data[0, sample_count/2-1], places = 6)
+ # last sample
+ self.assertAlmostEqual(4.20049, data[0, -1], places = 6)
+
+ # middle xline
+ middle_line = 2
+ # first sample
+ self.assertAlmostEqual(4.22, data[middle_line, 0], places = 5)
+ # middle sample
+ self.assertAlmostEqual(4.22024, data[middle_line, sample_count/2-1], places = 6)
+ # last sample
+ self.assertAlmostEqual(4.22049, data[middle_line, -1], places = 6)
+
+ # last xline
+ last_line = (len(f.xlines)-1)
+ # first sample
+ self.assertAlmostEqual(4.24, data[last_line, 0], places = 5)
+ # middle sample
+ self.assertAlmostEqual(4.24024, data[last_line, sample_count/2-1], places = 6)
+ # last sample
+ self.assertAlmostEqual(4.24049, data[last_line, sample_count-1], places = 6)
+
+ def test_xline_22(self):
+ with segyio.open(self.filename, "r") as f:
+
+ data = f.xline[22]
+
+ # first iline
+ # first sample
+ self.assertAlmostEqual(1.22, data[0, 0], places = 5)
+ # middle sample
+ self.assertAlmostEqual(1.22024, data[0, f.samples/2-1], places = 6)
+ # last sample
+ self.assertAlmostEqual(1.22049, data[0, f.samples-1], places = 6)
+
+ # middle iline
+ middle_line = 2
+ # first sample
+ self.assertAlmostEqual(3.22, data[middle_line, 0], places = 5)
+ # middle sample
+ self.assertAlmostEqual(3.22024, data[middle_line, f.samples/2-1], places = 6)
+ # last sample
+ self.assertAlmostEqual(3.22049, data[middle_line, f.samples-1], places = 6)
+
+ # last iline
+ last_line = len(f.ilines)-1
+ # first sample
+ self.assertAlmostEqual(5.22, data[last_line, 0], places = 5)
+ # middle sample
+ self.assertAlmostEqual(5.22024, data[last_line, f.samples/2-1], places = 6)
+ # last sample
+ self.assertAlmostEqual(5.22049, data[last_line, f.samples-1], places = 6)
+
+ def test_iline_slicing(self):
+ with segyio.open(self.filename, "r") as f:
+ self.assertEqual(len(f.ilines), sum(1 for _ in f.iline))
+ self.assertEqual(len(f.ilines), sum(1 for _ in f.iline[1:6]))
+ self.assertEqual(len(f.ilines), sum(1 for _ in f.iline[5:0:-1]))
+ self.assertEqual(len(f.ilines) / 2, sum(1 for _ in f.iline[0::2]))
+ self.assertEqual(len(f.ilines), sum(1 for _ in f.iline[1:]))
+ self.assertEqual(3, sum(1 for _ in f.iline[::2]))
+ self.assertEqual(0, sum(1 for _ in f.iline[12:24]))
+ self.assertEqual(3, sum(1 for _ in f.iline[:4]))
+ self.assertEqual(2, sum(1 for _ in f.iline[2:6:2]))
+
+ def test_xline_slicing(self):
+ with segyio.open(self.filename, "r") as f:
+ self.assertEqual(len(f.xlines), sum(1 for _ in f.xline))
+ self.assertEqual(len(f.xlines), sum(1 for _ in f.xline[20:25]))
+ self.assertEqual(len(f.xlines), sum(1 for _ in f.xline[25:19:-1]))
+ self.assertEqual(3, sum(1 for _ in f.xline[0::2]))
+ self.assertEqual(3, sum(1 for _ in f.xline[::2]))
+ self.assertEqual(len(f.xlines), sum(1 for _ in f.xline[20:]))
+ self.assertEqual(0, sum(1 for _ in f.xline[12:18]))
+ self.assertEqual(5, sum(1 for _ in f.xline[:25]))
+ self.assertEqual(2, sum(1 for _ in f.xline[:25:3]))
+
+ def test_open_transposed_lines(self):
+ il, xl = [], []
+ with segyio.open(self.filename, "r") as f:
+ il = f.ilines
+ xl = f.xlines
+
+ with segyio.open(self.filename, "r", segyio.TraceField.CROSSLINE_3D, segyio.TraceField.INLINE_3D) as f:
+ self.assertEqual(il, f.xlines)
+ self.assertEqual(xl, f.ilines)
+ pass
+
+
+ def test_file_info(self):
+ with segyio.open(self.filename, "r") as f:
+ self.assertEqual(2, f.sorting)
+ self.assertEqual(1, f.offsets)
+ self.assertEqual(1, int(f.format))
+
+ xlines = list(xrange(20, 25))
+ ilines = list(xrange(1, 6))
+ self.assertEqual(xlines, f.xlines)
+ self.assertEqual(ilines, f.ilines)
+ self.assertEqual(25, f.tracecount)
+ self.assertEqual(len(f.trace), f.tracecount)
+ self.assertEqual(50, f.samples)
+
+ def native_conversion(self):
+ arr1 = np.random.rand(10, dtype=np.float32)
+ arr2 = np.copy(arr1)
+ self.assertTrue(np.array_equal(arr1, arr2))
+
+ # round-trip should not modify data
+ segyio.file._from_native(1, f.samples, segyio.asfloatp(arr1))
+ self.assertFalse(np.array_equal(arr1, arr2))
+ segyio.file._to_native(1, f.samples, segyio.asfloatp(arr1))
+ self.assertTrue(np.array_equal(arr1, arr2))
+
+ def test_traces_slicing(self):
+ with segyio.open(self.filename, "r") as f:
+
+ traces = map(np.copy, f.trace[0:6:2])
+ self.assertEqual(len(traces), 3)
+ self.assertEqual(traces[0][49], f.trace[0][49])
+ self.assertEqual(traces[1][49], f.trace[2][49])
+ self.assertEqual(traces[2][49], f.trace[4][49])
+
+ rev_traces = map(np.copy, f.trace[4::-2])
+ self.assertEqual(rev_traces[0][49], f.trace[4][49])
+ self.assertEqual(rev_traces[1][49], f.trace[2][49])
+ self.assertEqual(rev_traces[2][49], f.trace[0][49])
+
+ # make sure buffers can be reused
+ buf = None
+ for i, trace in enumerate(f.trace[0:6:2, buf]):
+ self.assertTrue(np.array_equal(trace, traces[i]))
+
+ def test_line_generators(self):
+ with segyio.open(self.filename, "r") as f:
+ for line in f.iline:
+ pass
+
+ for line in f.xline:
+ pass
+
+ def test_read_header(self):
+ with segyio.open(self.filename, "r") as f:
+ self.assertEqual(1, f.header[0][189])
+ self.assertEqual(1, f.header[1][TraceField.INLINE_3D])
+
+ with self.assertRaises(IndexError):
+ f.header[0][188] # between byte offsets
+
+ with self.assertRaises(IndexError):
+ f.header[0][-1]
+
+ with self.assertRaises(IndexError):
+ f.header[0][700]
+
+ def test_write_header(self):
+ fname = self.filename + "write-header"
+ shutil.copyfile(self.filename, fname)
+ with segyio.open(fname, "r+") as f:
+ # assign to a field in a header, write immediately
+ f.header[0][189] = 42
+ f.flush()
+
+ self.assertEqual(42, f.header[0][189])
+ self.assertEqual(1, f.header[1][189])
+
+ # accessing non-existing offsets raises exceptions
+ with self.assertRaises(IndexError):
+ f.header[0][188] = 1 # between byte offsets
+
+ with self.assertRaises(IndexError):
+ f.header[0][-1] = 1
+
+ with self.assertRaises(IndexError):
+ f.header[0][700] = 1
+
+ d = { TraceField.INLINE_3D: 43,
+ TraceField.CROSSLINE_3D: 11,
+ TraceField.offset: 15 }
+
+ # assign multiple fields at once by using a dict
+ f.header[1] = d
+
+ f.flush()
+ self.assertEqual(43, f.header[1][TraceField.INLINE_3D])
+ self.assertEqual(11, f.header[1][TraceField.CROSSLINE_3D])
+ self.assertEqual(15, f.header[1][TraceField.offset])
+
+ # looking up multiple values at once returns a { TraceField: value } dict
+ self.assertEqual(d, f.header[1][TraceField.INLINE_3D, TraceField.CROSSLINE_3D, TraceField.offset])
+
+ # slice-support over headers (similar to trace)
+ for th in f.header[0:10]:
+ pass
+
+ self.assertEqual(6, len(list(f.header[10::-2])))
+ self.assertEqual(5, len(list(f.header[10:5:-1])))
+ self.assertEqual(0, len(list(f.header[10:5])))
+
+ # for-each support
+ for th in f.header:
+ pass
+
+ # copy a header
+ f.header[2] = f.header[1]
+ f.flush()
+ # don't use this interface in production code, it's only for testing
+ # i.e. don't access buf of treat it as a list
+ self.assertEqual(list(f.header[2].buf), list(f.header[1].buf))
+
+ def test_write_binary(self):
+ fname = self.filename.replace( ".sgy", "-binary.sgy")
+ shutil.copyfile(self.filename, fname)
+
+ with segyio.open(fname, "r+") as f:
+ f.bin[3213] = 5
+ f.flush()
+
+ self.assertEqual(5, f.bin[3213])
+
+ # accessing non-existing offsets raises exceptions
+ with self.assertRaises(IndexError):
+ f.bin[0]
+
+ with self.assertRaises(IndexError):
+ f.bin[50000]
+
+ with self.assertRaises(IndexError):
+ f.bin[3214]
+
+ d = { BinField.Traces: 43,
+ BinField.SweepFrequencyStart: 11 }
+
+ # assign multiple fields at once by using a dict
+ f.bin = d
+
+ f.flush()
+ self.assertEqual(43, f.bin[BinField.Traces])
+ self.assertEqual(11, f.bin[BinField.SweepFrequencyStart])
+
+ # looking up multiple values at once returns a { TraceField: value } dict
+ self.assertEqual(d, f.bin[BinField.Traces, BinField.SweepFrequencyStart])
+
+ # copy a header
+ f.bin = f.bin
+
+ def test_fopen_error(self):
+ # non-existent file
+ with self.assertRaises(OSError):
+ segyio.open("no_dir/no_file", "r")
+
+ # non-existant mode
+ with self.assertRaises(OSError):
+ segyio.open(self.filename, "foo")
+
+ def test_wrong_lineno(self):
+ with self.assertRaises(KeyError):
+ with segyio.open(self.filename, "r") as f:
+ f.iline[3000]
+
+ with self.assertRaises(KeyError):
+ with segyio.open(self.filename, "r") as f:
+ f.xline[2]
+
+ def test_open_wrong_inline(self):
+ with self.assertRaises(ValueError):
+ with segyio.open(self.filename, "r", 2) as f:
+ pass
+
+ def test_open_wrong_crossline(self):
+ with self.assertRaises(ValueError):
+ with segyio.open(self.filename, "r", 189, 2) as f:
+ pass
+
+ def test_create_sgy(self):
+ dstfile = self.filename.replace(".sgy", "-created.sgy")
+
+ with segyio.open(self.filename, "r") as src:
+ spec = segyio.spec()
+ spec.format = int(src.format)
+ spec.sorting = int(src.sorting)
+ spec.samples = src.samples
+ spec.ilines = src.ilines
+ spec.xlines = src.xlines
+
+ with segyio.create(dstfile, spec) as dst:
+ dst.text[0] = src.text[0]
+ dst.bin = src.bin
+
+ # copy all headers
+ dst.header = src.header
+
+ for i, srctr in enumerate(src.trace):
+ dst.trace[i] = srctr
+
+ dst.trace = src.trace
+
+ # this doesn't work yet, some restructuring is necessary
+ # if it turns out to be a desired feature it's rather easy to do
+ #for dsth, srch in zip(dst.header, src.header):
+ # dsth = srch
+
+ #for dsttr, srctr in zip(dst.trace, src.trace):
+ # dsttr = srctr
+
+ self.assertTrue(filecmp.cmp(self.filename, dstfile))
+
+ def test_create_sgy_shorter_traces(self):
+ dstfile = self.filename.replace(".sgy", "-shorter.sgy")
+
+ with segyio.open(self.filename, "r") as src:
+ spec = segyio.spec()
+ spec.format = int(src.format)
+ spec.sorting = int(src.sorting)
+ spec.samples = 20 # reduces samples per trace
+ spec.ilines = src.ilines
+ spec.xlines = src.xlines
+
+ with segyio.create(dstfile, spec) as dst:
+ for i, srch in enumerate(src.header):
+ dst.header[i] = srch
+ d = { TraceField.INLINE_3D: srch[TraceField.INLINE_3D] + 100 }
+ dst.header[i] = d
+
+ for lineno in dst.ilines:
+ dst.iline[lineno] = src.iline[lineno]
+
+ # alternative form using left-hand-side slices
+ dst.iline[2:4] = src.iline
+
+
+ buf = None # reuse buffer for speed
+ for lineno in dst.xlines:
+ dst.xline[lineno] = src.xline[lineno, buf]
+
+ with segyio.open(dstfile, "r") as dst:
+ self.assertEqual(20, dst.samples)
+ self.assertEqual([x + 100 for x in src.ilines], dst.ilines)
+
+ def test_create_from_naught(self):
+ fname = "test-data/mk.sgy"
+ spec = segyio.spec()
+ spec.format = 5
+ spec.sorting = 2
+ spec.samples = 150
+ spec.ilines = range(1, 11)
+ spec.xlines = range(1, 6)
+
+ with segyio.create(fname, spec) as dst:
+ tr = np.arange( start = 1.000, stop = 1.151, step = 0.001, dtype = np.float32 )
+
+ for i in xrange( len( dst.trace ) ):
+ dst.trace[i] = tr
+ tr += 1.000
+
+ for il in spec.ilines:
+ dst.header.iline[il] = { TraceField.INLINE_3D: il }
+
+ for xl in spec.xlines:
+ dst.header.xline[xl] = { TraceField.CROSSLINE_3D: xl }
+
+ # Set header field 'offset' to 1 in all headers
+ dst.header = { TraceField.offset: 1 }
+
+ with segyio.open(fname, "r") as f:
+ self.assertAlmostEqual(1, f.trace[0][0], places = 4)
+ self.assertAlmostEqual(1.001, f.trace[0][1], places = 4)
+ self.assertAlmostEqual(1.149, f.trace[0][-1], places = 4)
+ self.assertAlmostEqual(50.100, f.trace[-1][100], places = 4)
+ self.assertEqual(f.header[0][TraceField.offset], f.header[1][TraceField.offset])
+ self.assertEqual(1, f.header[1][TraceField.offset])
+
+ @staticmethod
+ def mklines(fname):
+ spec = segyio.spec()
+ spec.format = 5
+ spec.sorting = 2
+ spec.samples = 10
+ spec.ilines = range(1, 11)
+ spec.xlines = range(1, 6)
+
+# create a file with 10 inlines, with values on the form l.0tv where
+# l = line no
+# t = trace number (within line)
+# v = trace value
+# i.e. 2.043 is the value at inline 2's fourth trace's third value
+ with segyio.create(fname, spec) as dst:
+ ln = np.arange(start = 0,
+ stop = 0.001 * (5 * 10),
+ step = 0.001,
+ dtype = np.float32).reshape(5, 10)
+
+ for il in spec.ilines:
+ ln += 1
+
+ dst.header.iline[il] = { TraceField.INLINE_3D: il }
+ dst.iline[il] = ln
+
+ for xl in spec.xlines:
+ dst.header.xline[xl] = { TraceField.CROSSLINE_3D: xl }
+
+ def test_create_write_lines(self):
+ fname = "test-data/mklines.sgy"
+
+ self.mklines(fname)
+
+ with segyio.open(fname, "r") as f:
+ self.assertAlmostEqual(1, f.iline[1][0][0], places = 4)
+ self.assertAlmostEqual(2.004, f.iline[2][0][4], places = 4)
+ self.assertAlmostEqual(2.014, f.iline[2][1][4], places = 4)
+ self.assertAlmostEqual(8.043, f.iline[8][4][3], places = 4)
+
+ def test_create_sgy_skip_lines(self):
+ fname = "test-data/lines.sgy"
+ dstfile = fname.replace(".sgy", "-halved.sgy")
+
+ self.mklines(fname)
+
+ with segyio.open(fname, "r") as src:
+ spec = segyio.spec()
+ spec.format = int(src.format)
+ spec.sorting = int(src.sorting)
+ spec.samples = src.samples
+ spec.ilines = src.ilines[::2]
+ spec.xlines = src.xlines[::2]
+
+ with segyio.create(dstfile, spec) as dst:
+ # use the inline headers as base
+ dst.header.iline = src.header.iline[::2]
+ # then update crossline numbers from the crossline headers
+ for xl in dst.xlines:
+ f = next(src.header.xline[xl])[TraceField.CROSSLINE_3D]
+ dst.header.xline[xl] = { TraceField.CROSSLINE_3D: f }
+
+ # but we override the last xline to be 6, not 5
+ dst.header.xline[5] = { TraceField.CROSSLINE_3D: 6 }
+ dst.iline = src.iline[::2]
+
+ with segyio.open(dstfile, "r") as f:
+ self.assertEqual(f.ilines, spec.ilines)
+ self.assertEqual(f.xlines, [1,3,6])
+ self.assertAlmostEqual(1, f.iline[1][0][0], places = 4)
+ self.assertAlmostEqual(3.004, f.iline[3][0][4], places = 4)
+ self.assertAlmostEqual(3.014, f.iline[3][1][4], places = 4)
+ self.assertAlmostEqual(7.023, f.iline[7][2][3], places = 4)
diff --git a/tests/test_segy_mex.m b/tests/test_segy_mex.m
new file mode 100644
index 0000000..7b4dc15
--- /dev/null
+++ b/tests/test_segy_mex.m
@@ -0,0 +1,230 @@
+% test segyline
+
+% preconditions
+filename = 'test-data/small.sgy';
+assert(exist(filename,'file')==2);
+t0 = 1111.0;
+
+%% Spec is created
+try
+ spec = Segy.interpret_segycube(filename, 'INLINE_3D', 'CROSSLINE_3D', t0);
+ spec = Segy.interpret_segycube(filename, TraceField.INLINE_3D, 'CROSSLINE_3D', t0);
+ spec = Segy.interpret_segycube(filename, TraceField.INLINE_3D, 193, t0);
+ data = Segy.get_cube( spec );
+
+ assert(all(size(data) == [50, 5, 5]));
+catch
+ %nothing should be caught
+ assert(false);
+end
+
+% fail when file doesn't exist
+
+try
+ Segy.get_header('does-not-exist', 193);
+ assert(false);
+catch
+ assert(true);
+end
+
+try
+ Segy.get_traces('does-not-exist', 189, 193 );
+ assert(false);
+catch
+ assert(true);
+end
+
+try
+ Segy.get_ntraces('does-not-exist');
+ assert(false);
+catch
+ assert(true);
+end
+
+try
+ Segy.interpret_segycube('does-not-exist');
+ assert(false);
+catch
+ assert(true);
+end
+
+%% Segy.readInLine 4
+
+spec = Segy.interpret_segycube(filename, 'INLINE_3D', 'CROSSLINE_3D', t0);
+data = Segy.get_line(spec, 'iline', 4);
+sample_count = length(spec.sample_indexes);
+
+eps = 1e-4;
+% first trace along xline
+% first sample
+assert(abs(data(1, 1) - 4.2)<eps);
+% middle sample
+assert(abs(data(sample_count/2,1)-4.20024)<eps);
+% last sample
+assert(abs(data(sample_count,1)-4.20049)<eps);
+
+% middle trace along xline
+middle = 3;
+% first sample
+assert(abs(data(1, middle) - 4.22) < eps);
+% middle sample
+assert(abs(data(sample_count/2,middle)-4.22024)<eps);
+% last sample
+assert(abs(data(sample_count,middle)-4.22049)<eps);
+
+% middle trace along xline
+last = length(spec.crossline_indexes);
+% first sample
+assert(abs(data(1, last) - 4.24) < eps);
+% middle sample
+assert(abs(data(sample_count/2,last)-4.24024)<eps);
+% last sample
+assert(abs(data(sample_count,last)-4.24049)<eps);
+
+%% Segy.readCrossLine 1433
+
+spec = SegySpec(filename, TraceField.INLINE_3D, TraceField.CROSSLINE_3D, t0);
+data = Segy.readCrossLine(spec, 20);
+data = Segy.readCrossLine(spec, 21);
+data = Segy.readCrossLine(spec, 22);
+data = Segy.readCrossLine(spec, 23);
+data = Segy.readCrossLine(spec, 24);
+data = Segy.readCrossLine(spec, 22);
+sample_count = length(spec.sample_indexes);
+
+eps = 1e-4;
+% first trace along iline
+% first sample
+assert(abs(data(1, 1) - 1.22) < eps);
+% middle sample
+assert(abs(data(sample_count/2,1)-1.22024)<eps);
+% last sample
+assert(abs(data(sample_count,1)-1.22049)<eps);
+
+% middle trace along iline
+middle = 3;
+% first sample
+assert(abs(data(1, middle) - 3.22) < eps);
+% middle sample
+assert(abs(data(sample_count/2,middle)-3.22029)<eps);
+% last sample
+assert(abs(data(sample_count,middle)-3.22049)<eps);
+
+% middle trace along iline
+last = length(spec.inline_indexes);
+% first sample
+assert(abs(data(1, last) - 5.22) < eps);
+% middle sample
+assert(abs(data(sample_count/2,last)-5.22029)<eps);
+% last sample
+assert(abs(data(sample_count,last)-5.22049)<eps);
+
+filename_write = 'test-data/SEGY-3D_write_mex.sgy';
+
+copyfile(filename, filename_write)
+
+spec = SegySpec(filename_write, TraceField.INLINE_3D, TraceField.CROSSLINE_3D, t0);
+data = Segy.get_line(spec, 'xline', 22);
+
+assert(abs(data(1, 1) - 1.22) < eps);
+
+data(1,1) = 100;
+
+Segy.writeCrossLine(spec, data, 22);
+
+data = Segy.readCrossLine(spec, 22);
+assert(data(1, 1) == 100);
+
+data = Segy.readInLine(spec, 4);
+
+assert(abs(data(1, 1) - 4.2) < eps);
+
+data(1,1) = 200;
+
+Segy.writeInLine(spec, data, 4);
+
+data = Segy.readInLine(spec, 4);
+assert(data(1, 1) == 200);
+
+[~, dt, nt] = Segy.get_traces(filename);
+assert(dt == 4000);
+assert(nt == 1);
+
+[headers, notraces] = Segy.get_header(filename, 'INLINE_3D');
+assert(isequal((1:5), unique(headers)));
+assert(notraces == 25)
+
+assert(Segy.get_ntraces(filename) == 25);
+
+
+% Goal:
+% Fast writing of segy file
+%
+% Inputs:
+% filename Filename of segyfile to write
+% filename_orig Filename of segyfile to copy header
+% data: Data
+% nt: Number of time samples
+% nxl: Number of Xlines
+% nil: Number of Inlines
+%
+% function provided by Matteo Ravasi
+
+Segy_struct_orig = Segy.interpret_segycube(filename,'INLINE_3D','CROSSLINE_3D');
+data = Segy.get_traces(filename);
+data = data + 1000;
+
+nt = 50;
+nxl = 5;
+nil = 5;
+
+if( ( nt == numel( Segy_struct_orig.t ) ) &&...
+ ( nxl == numel( Segy_struct_orig.xline ) ) &&...
+ ( nil == numel( Segy_struct_orig.iline ) ) )
+
+ data = reshape( data, [nt, nxl*nil] );
+
+ filename_copy = 'test-data/SEGY-3D_copy.sgy';
+ copyfile( filename, filename_copy );
+
+ Segy.put_traces( filename_copy, data, 1, nxl*nil );
+ spec = Segy.interpret_segycube( filename_copy );
+ data = Segy.get_line(spec, 'iline', 4);
+
+ assert(abs(data(1, 1) - 1004.2) < eps);
+ assert(abs(data(sample_count/2,1) - 1004.20024) < eps);
+ assert(abs(data(sample_count,1) - 1004.20049) < eps);
+
+ middle = 3;
+ assert(abs(data(1, middle) - 1004.22) < eps);
+ assert(abs(data(sample_count/2, middle) - 1004.22024) < eps);
+ assert(abs(data(sample_count, middle) - 1004.22049) < eps);
+
+ last = length(spec.crossline_indexes);
+ assert(abs(data(1, last) - 1004.24) < eps);
+ assert(abs(data(sample_count/2, last) - 1004.24024) < eps);
+ assert(abs(data(sample_count, last) - 1004.24049) < eps);
+else
+ assert(false);
+end
+
+% test put_line
+data = Segy.get_line(spec, 'iline', 4);
+data = data + 100;
+p1 = data(sample_count/2, 1);
+p2 = data(sample_count, 1);
+Segy.put_line(spec, data, 'iline', 4);
+data = Segy.get_line( spec, 'iline', 4);
+assert(all([p1, p2] == [data(sample_count/2, 1), data(sample_count, 1)]));
+
+% read trace headers and file headers
+dummy = Segy.get_segy_header( filename );
+dummy = Segy.get_trace_header( filename, 0 );
+dummy = Segy.get_trace_header( filename, 10 );
+
+Segy.put_headers( filename, 10, 'CDP' );
+Segy.get_header( filename, 'CDP' );
+
+increasing = linspace( 1, notraces, notraces );
+Segy.put_headers( filename_copy, increasing, 'offset' );
+assert( all(increasing == Segy.get_header( filename_copy, 'offset' )) );
diff --git a/tests/test_segyspec.c b/tests/test_segyspec.c
new file mode 100644
index 0000000..9ed8477
--- /dev/null
+++ b/tests/test_segyspec.c
@@ -0,0 +1,79 @@
+#include <stdlib.h>
+#include <string.h>
+
+#include "unittest.h"
+
+#include <spec/segyspec.h>
+#include <segyio/segy.h>
+
+
+void testSegyInspection() {
+ const char *path = "test-data/small.sgy";
+ double t0 = 1111.0;
+
+ SegySpec spec;
+
+ segyCreateSpec(&spec, path, INLINE_3D, CROSSLINE_3D, t0);
+
+ assertTrue(spec.sample_format == IBM_FLOAT_4_BYTE, "Expected the float format to be IBM Float");
+
+ assertTrue(strcmp(spec.filename, path) == 0, "The paths did not match");
+
+ assertTrue(spec.offset_count == 1, "Expected offset to be 1");
+
+ assertTrue(spec.trace_sorting_format == INLINE_SORTING, "Expected sorting to be INLINE_SORTING");
+
+
+ assertTrue(spec.sample_count == 50, "Expected sample count to be 50");
+
+ for(int i = 0; i < spec.sample_count; i++) {
+ double t = t0 + i * 4.0;
+ assertTrue(spec.sample_indexes[i] == t, "Sample index not equal to expected value");
+ }
+
+ assertTrue(spec.inline_count == 5, "Expect inline count to be 5");
+ for(int i = 0; i < spec.inline_count; i++) {
+ unsigned int il = spec.inline_indexes[i];
+ assertTrue(il >= 1 && il <= 5, "Expected inline index value to be between [1, 5]");
+ }
+
+ assertTrue(spec.crossline_count == 5, "Expect crossline count to be 5");
+ for(int i = 0; i < spec.crossline_count; i++) {
+ unsigned int xl = spec.crossline_indexes[i];
+ assertTrue(xl >= 20 && xl <= 24, "Expected crossline index value to be between [20, 24]");
+ }
+
+ if (spec.crossline_indexes != NULL)
+ free(spec.crossline_indexes);
+ if (spec.inline_indexes != NULL)
+ free(spec.inline_indexes);
+ if (spec.sample_indexes != NULL)
+ free(spec.sample_indexes);
+
+ free(spec.filename);
+
+}
+
+void testAlloc(){
+ const char *path = "test-data/small.sgy";
+ double t0 = 1111.0;
+ SegySpec spec;
+ segyCreateSpec(&spec, path, INLINE_3D, CROSSLINE_3D, t0);
+
+ if (spec.crossline_indexes != NULL)
+ free(spec.crossline_indexes);
+ if (spec.inline_indexes != NULL)
+ free(spec.inline_indexes);
+ if (spec.sample_indexes != NULL)
+ free(spec.sample_indexes);
+
+ free(spec.filename);
+
+}
+
+int main() {
+ testAlloc();
+ testSegyInspection();
+ exit(0);
+}
+
diff --git a/tests/test_segyspec_mex.m b/tests/test_segyspec_mex.m
new file mode 100644
index 0000000..e3c29d0
--- /dev/null
+++ b/tests/test_segyspec_mex.m
@@ -0,0 +1,88 @@
+% test segyspec
+
+% preconditions
+filename = 'test-data/small.sgy';
+assert(exist(filename,'file')==2);
+t0 = 1111.0;
+
+%% no such file
+no_such_filename = 'no-such-dir/no-such-file.sgy';
+assert(exist(no_such_filename,'file')~=2);
+try
+ spec = SegySpec(no_such_filename, TraceField.INLINE_3D, TraceField.CROSSLINE_3D, t0);
+ %should not reach here
+ assert(false);
+catch
+ %not actually needed...
+ assert(true);
+end
+
+%% Spec is created
+try
+ spec = SegySpec(filename, TraceField.INLINE_3D, TraceField.CROSSLINE_3D, t0);
+catch
+ %nothing should be caught
+ assert(false);
+end
+
+%% IBM_FLOAT_4_BYTE
+spec = SegySpec(filename, TraceField.INLINE_3D, TraceField.CROSSLINE_3D, t0);
+assert(spec.sample_format == SegySampleFormat.IBM_FLOAT_4_BYTE);
+
+%% filename is set
+spec = SegySpec(filename, TraceField.INLINE_3D, TraceField.CROSSLINE_3D, t0);
+assert(strcmp(spec.filename,filename));
+
+%% trace_sorting_format
+spec = SegySpec(filename, TraceField.INLINE_3D, TraceField.CROSSLINE_3D, t0);
+assert(spec.trace_sorting_format == TraceSortingFormat.INLINE);
+
+%%offset_count
+spec = SegySpec(filename, TraceField.INLINE_3D, TraceField.CROSSLINE_3D, t0);
+assert(length(spec.offset_count) == 1);
+
+%% samples
+spec = SegySpec(filename, TraceField.INLINE_3D, TraceField.CROSSLINE_3D, t0);
+sample_indexes = spec.sample_indexes;
+assert(length(sample_indexes) == 50);
+
+%% first_trace_pos
+spec = SegySpec(filename, TraceField.INLINE_3D, TraceField.CROSSLINE_3D, t0);
+first_trace_pos = spec.first_trace_pos;
+assert(first_trace_pos == 3600);
+
+%% il_stride
+spec = SegySpec(filename, TraceField.INLINE_3D, TraceField.CROSSLINE_3D, t0);
+il_stride = spec.il_stride;
+assert(il_stride == 1);
+
+%% xl_stride
+spec = SegySpec(filename, TraceField.INLINE_3D, TraceField.CROSSLINE_3D, t0);
+xl_stride = spec.xl_stride;
+assert(xl_stride == 5);
+
+%% xl_stride
+spec = SegySpec(filename, TraceField.INLINE_3D, TraceField.CROSSLINE_3D, t0);
+trace_bsize = spec.trace_bsize;
+assert(trace_bsize == 50*4);
+
+
+for i = 1:length(sample_indexes)
+ t = t0 + (i-1) * 4;
+ assert(sample_indexes(i) == t);
+end
+
+%% xline
+spec = SegySpec(filename, TraceField.INLINE_3D, TraceField.CROSSLINE_3D, t0);
+assert(length(spec.crossline_indexes)==5)
+for xl = spec.crossline_indexes'
+ assert(xl >= 20 && xl <= 24);
+end
+
+%% iline
+spec = SegySpec(filename, TraceField.INLINE_3D, TraceField.CROSSLINE_3D, t0);
+assert(length(spec.inline_indexes)==5)
+
+for il = spec.inline_indexes'
+ assert(il >= 1 && il <= 5);
+end
diff --git a/tests/test_utils.c b/tests/test_utils.c
new file mode 100644
index 0000000..a88164d
--- /dev/null
+++ b/tests/test_utils.c
@@ -0,0 +1,128 @@
+#include <stdlib.h>
+#include <string.h>
+#include <segyio/segy.h>
+#include "unittest.h"
+
+void ebcdic2ascii( const char*, char* );
+void ascii2ebcdic( const char*, char* );
+
+void testEbcdicConversion() {
+ char* expected = (char*) "Hello there!";
+ char str[] = "\xc8\x85\x93\x93\x96\x40\xa3\x88\x85\x99\x85\x4f";
+
+ char result[strlen(expected)];
+
+ ebcdic2ascii(str, result);
+ assertTrue(strcmp(result, expected) == 0, "Converted string did not match the expected result!");
+
+ ascii2ebcdic(result, result);
+ assertTrue(strcmp(result, str) == 0, "Converted string did not match the expected result!");
+}
+
+void testEbcdicTable() {
+ char ascii[256];
+ for (unsigned char i = 0; i < 255; i++) {
+ ascii[i] = (char) (i + 1);
+ }
+ ascii[255] = 0;
+
+ char ebcdic[256];
+ ascii2ebcdic((const char*) ascii, ebcdic);
+
+ char result_ascii[256];
+ ebcdic2ascii(ebcdic, result_ascii);
+
+ assertTrue(strcmp(ascii, result_ascii) == 0, "Conversion from EBCDIC to ASCII to EBCDIC failed!");
+ for(unsigned char i = 0; i < 255; i++) {
+ }
+}
+
+void testConversionAllocation() {
+ char* expected = (char*) "Hello there!";
+ char str[] = "\xc8\x85\x93\x93\x96\x40\xa3\x88\x85\x99\x85\x4f";
+
+ char result[strlen(str) + 1];
+ ebcdic2ascii(str, result);
+ assertTrue(strcmp(result, expected) == 0, "Converted string did not match the expected result!");
+
+ ascii2ebcdic(expected, result);
+ assertTrue(strcmp(result, str) == 0, "Converted string did not match the expected result!");
+}
+
+#define MAX 1000000 /* number of iterations */
+#define IBM_EPS 4.7683738e-7 /* worst case error */
+
+void ibm2ieee(void* to, const void* from, int len);
+void ieee2ibm(void* to, const void* from, int len);
+
+static void check(float f1, double * epsm) {
+ int exp;
+ float f2;
+ double eps;
+ unsigned ibm1, ibm2;
+
+ frexp(f1, &exp);
+ ieee2ibm(&ibm1, &f1, 1);
+ ibm2ieee(&f2, &ibm1, 1);
+ ieee2ibm(&ibm2, &f2, 1);
+
+ assertTrue(memcmp(&ibm1, &ibm2, sizeof ibm1) == 0, "The content of two memory areas were not identical!");
+ //printf("Error: %08x <=> %08x\n", *(unsigned*) &ibm1, *(unsigned*) &ibm2);
+
+ eps = ldexp(fabs(f1 - f2), -exp);
+ if (eps > *epsm) {
+ *epsm = eps;
+ }
+
+ assertTrue(eps < IBM_EPS, "Difference over conversion larger than allowed epsilon!");
+ //printf("Error: %.8g != %.8g\n", f1, f2);
+}
+
+void testIBMFloat() {
+ int i;
+ float f1;
+
+ double epsm = 0.0;
+ for (i = 0; i < MAX; i++) {
+ f1 = rand();
+ check(f1, &epsm);
+ check(-f1, &epsm);
+ }
+ printf("Max eps: %g\n", epsm);
+}
+
+int to_int16( const char* );
+int to_int32( const char* );
+
+int16_t from_int16( int16_t );
+int32_t from_int32( int32_t );
+
+void test_integer_round_trip() {
+ /* this test probably only works as expected on intel/x86 */
+ /* this is what data looks like when read from segy */
+ char buf16[ 2 ] = { 000, 001 }; /* 1 */
+ /* unsigned to avoid overflow warning on 0257 */
+ unsigned char buf32[ 4 ] = { 0, 0, 011, 0257 };
+
+ int i1 = to_int16( buf16 );
+ int i2479 = to_int32( (char*)buf32 );
+ assertTrue( i1 == 1, "Expected SEGY two's complement 2-byte 1 => 1" );
+ assertTrue( i2479 == 2479, "Expected SEGY two's complement 4-byte 2479 => 2479" );
+
+ int16_t round_int16 = from_int16( 1 );
+ int32_t round_int32 = from_int32( 2479 );
+
+ assertTrue( memcmp( &round_int16, buf16, sizeof( round_int16 ) ) == 0,
+ "int16 did not survive round trip" );
+ assertTrue( memcmp( &round_int32, buf32, sizeof( round_int32 ) ) == 0,
+ "int32 did not survive round trip" );
+}
+
+int main() {
+ testEbcdicConversion();
+ testEbcdicTable();
+ testConversionAllocation();
+ testIBMFloat();
+ test_integer_round_trip();
+ exit(0);
+}
diff --git a/tests/unittest.h b/tests/unittest.h
new file mode 100644
index 0000000..c0e3194
--- /dev/null
+++ b/tests/unittest.h
@@ -0,0 +1,43 @@
+#ifndef SEGYIO_UNITTEST_H
+#define SEGYIO_UNITTEST_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <string.h>
+#include <math.h>
+
+void testAssertionFailed(const char *message, const char *file, int line) {
+ fprintf(stderr, "Assertion failed in file: %s on line: %d\n", file, line);
+ if (strlen(message) > 0) {
+ fprintf(stderr, message);
+ }
+ exit(1);
+}
+
+#define assertTrue(value, message) _testAssertTrue((value), (message), __FILE__, __LINE__)
+
+void _testAssertTrue(bool value, const char *message, const char *file, int line) {
+ if (!value) {
+ if (strlen(message) == 0) {
+ message = "The expression did not evaluate to true!";
+ }
+ testAssertionFailed(message, file, line);
+ }
+}
+
+#define assertClose(expected, actual, eps) _testAssertClose((expected), (actual), (eps), __FILE__, __LINE__)
+
+void _testAssertClose(float expected, float actual, float eps, const char *file, int line) {
+ float diff = fabsf(expected-actual);
+ if (!(diff <= eps)) {
+ char message[1000];
+ sprintf(message, "Expected: %f, Actual: %f, diff: %f, eps: %f\n", expected, actual, diff, eps);
+ testAssertionFailed(message, file, line);
+ }
+}
+
+
+
+#endif //SEGYIO_UNITTEST_H
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-science/packages/segyio.git
More information about the debian-science-commits
mailing list