Skip to content

Commit

Permalink
Move Channel class to a separated file
Browse files Browse the repository at this point in the history
  • Loading branch information
seisman committed Mar 27, 2024
1 parent 683a84a commit 97b0180
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 116 deletions.
118 changes: 118 additions & 0 deletions HinetPy/channel.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import logging
import math

# Setup the logger
FORMAT = "[%(asctime)s] %(levelname)s: %(message)s"
logging.basicConfig(level=logging.INFO, format=FORMAT, datefmt="%Y-%m-%d %H:%M:%S")
logger = logging.getLogger(__name__)


class Channel:
"""
Class for channel information.
"""

def __init__( # noqa: PLR0913
self,
id=None, # noqa: A002
name=None,
component=None,
latitude=None,
longitude=None,
unit=None,
gain=None,
damping=None,
period=None,
preamplification=None,
lsb_value=None,
):
"""
Initialize a channel.
Parameters
----------
id: str
Channel ID.
name: str
Station Name.
component: str
Channel component name (e.g., ``U``, ``N`` or ``E``).
latitude: float
Station latitude.
longitude: float
Station longitude.
unit: str
Unit of data (e.g., ``m``, ``m/s``, ``m/s/s``, ``rad``).
gain: float
Sensor sensitivity.
damping: float
Damping constant of the sensor.
period: float
Natural period of the seismometer.
preamplification:
Preamplification value.
lsb_value:
LSB value.
Notes
-----
The Hi-net website uses the moving-coil velocity-type seismometer.
See :doc:`/appendix/response` for details.
"""
self.id = id
self.name = name
self.component = component
self.latitude = latitude
self.longitude = longitude
self.unit = unit
self.gain = gain
self.damping = damping
self.period = period
self.preamplification = preamplification
self.lsb_value = lsb_value

def write_sacpz(self, pzfile, keep_sensitivity=False):
"""
Write channel information into a SAC polezero file.
Parameters
----------
pzfile: str
Name of the SAC polezero file.
keep_sensitivity: bool
Keep sensitivity in the SAC polezero "CONSTANT" or not.
"""
chan_info = f"{self.name}.{self.component} ({self.id})"
# Hi-net uses a moving-coil velocity-type seismometer.
if self.unit != "m/s":
logger.warning(
"%s: Unit is not velocity. The PZ file may be wrong.", chan_info
)

try:
freq = 2.0 * math.pi / self.period
except ZeroDivisionError:
logger.warning("%s: Natural period = 0. Skipped.", chan_info)
return

# calculate poles by finding roots of equation s^2+2hws+w^2=0
real = 0.0 - self.damping * freq
imaginary = freq * math.sqrt(1.0 - self.damping**2.0)

# calculate the CONSTANT
fn = 20.0 # alaways assume normalization frequency is 20 Hz
s = complex(0, 2 * math.pi * fn)
a0 = abs((s**2 + 2 * self.damping * freq * s + freq**2) / s**2)
if keep_sensitivity:
factor = math.pow(10, self.preamplification / 20.0)
constant = a0 * self.gain * factor / self.lsb_value
else:
constant = a0

# write information to a SAC PZ file
with open(pzfile, "w", encoding="utf8") as pz:
pz.write("ZEROS 3\n")
pz.write("POLES 2\n")
pz.write(f"{real:9.6f} {imaginary:9.6f}\n")
pz.write(f"{real:9.6f} {-imaginary:9.6f}\n")
pz.write(f"CONSTANT {constant:e}\n")
114 changes: 2 additions & 112 deletions HinetPy/win32.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,131 +4,21 @@

import glob
import logging
import math
import os
import subprocess
import tempfile
from fnmatch import fnmatch
from multiprocessing import Pool
from subprocess import DEVNULL, PIPE, Popen

from .channel import Channel

# Setup the logger
FORMAT = "[%(asctime)s] %(levelname)s: %(message)s"
logging.basicConfig(level=logging.INFO, format=FORMAT, datefmt="%Y-%m-%d %H:%M:%S")
logger = logging.getLogger(__name__)


class Channel:
"""
Class for channel information.
"""

def __init__( # noqa: PLR0913
self,
id=None, # noqa: A002
name=None,
component=None,
latitude=None,
longitude=None,
unit=None,
gain=None,
damping=None,
period=None,
preamplification=None,
lsb_value=None,
):
"""
Initialize a channel.
Parameters
----------
id: str
Channel ID.
name: str
Station Name.
component: str
Channel component name (e.g., ``U``, ``N`` or ``E``).
latitude: float
Station latitude.
longitude: float
Station longitude.
unit: str
Unit of data (e.g., ``m``, ``m/s``, ``m/s/s``, ``rad``).
gain: float
Sensor sensitivity.
damping: float
Damping constant of the sensor.
period: float
Natural period of the seismometer.
preamplification:
Preamplification value.
lsb_value:
LSB value.
Notes
-----
The Hi-net website uses the moving-coil velocity-type seismometer.
See :doc:`/appendix/response` for details.
"""
self.id = id
self.name = name
self.component = component
self.latitude = latitude
self.longitude = longitude
self.unit = unit
self.gain = gain
self.damping = damping
self.period = period
self.preamplification = preamplification
self.lsb_value = lsb_value

def write_sacpz(self, pzfile, keep_sensitivity=False):
"""
Write channel information into a SAC polezero file.
Parameters
----------
pzfile: str
Name of the SAC polezero file.
keep_sensitivity: bool
Keep sensitivity in the SAC polezero "CONSTANT" or not.
"""
chan_info = f"{self.name}.{self.component} ({self.id})"
# Hi-net uses a moving-coil velocity-type seismometer.
if self.unit != "m/s":
logger.warning(
"%s: Unit is not velocity. The PZ file may be wrong.", chan_info
)

try:
freq = 2.0 * math.pi / self.period
except ZeroDivisionError:
logger.warning("%s: Natural period = 0. Skipped.", chan_info)
return

# calculate poles by finding roots of equation s^2+2hws+w^2=0
real = 0.0 - self.damping * freq
imaginary = freq * math.sqrt(1.0 - self.damping**2.0)

# calculate the CONSTANT
fn = 20.0 # alaways assume normalization frequency is 20 Hz
s = complex(0, 2 * math.pi * fn)
a0 = abs((s**2 + 2 * self.damping * freq * s + freq**2) / s**2)
if keep_sensitivity:
factor = math.pow(10, self.preamplification / 20.0)
constant = a0 * self.gain * factor / self.lsb_value
else:
constant = a0

# write information to a SAC PZ file
with open(pzfile, "w", encoding="utf8") as pz:
pz.write("ZEROS 3\n")
pz.write("POLES 2\n")
pz.write(f"{real:9.6f} {imaginary:9.6f}\n")
pz.write(f"{real:9.6f} {-imaginary:9.6f}\n")
pz.write(f"CONSTANT {constant:e}\n")


def extract_sac(
data,
ctable,
Expand Down
1 change: 1 addition & 0 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ API Reference

api/HinetPy.client.rst
api/HinetPy.win32.rst
api/HinetPy.channel.rst
api/HinetPy.header.rst
api/HinetPy.utils.rst
6 changes: 6 additions & 0 deletions docs/api/HinetPy.channel.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
HinetPy.channel module
----------------------

.. automodule:: HinetPy.channel
:members:
:undoc-members:
6 changes: 2 additions & 4 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ Changelog

0.7.0 (2022-07-01):
- Fix the incorrect maximum allowed time span for F-net (#65)
- `get_selected_stations` now returns a list of stations with station metadata
information (#36)
- `get_selected_stations` now returns a list of stations with station metadata information (#36)
- Refactor the `_channel2pz()` and `_write_pz()` functions to `Channel.write_sacpz()`
- Refactor the `_get_channels` function to `win32.read_ctable()`
- The `win32.extrac_sacpz` function now supports parallel data processing
Expand Down Expand Up @@ -93,8 +92,7 @@ Changelog

0.4.0 (2017-04-01):
- ``win32.extract_sac()``: skip if data not exists
- ``win32.extract_sac()``: support multiple processes to speedup, and
no longer return values
- ``win32.extract_sac()``: support multiple processes to speedup, and no longer return values
- ``Client.get_waveform()``: support multi-threads to speedup
- Change ``Client.help()`` to ``Client.info()``
- ``Client.get_waveform()`` now can automatically set ``max_span``
Expand Down

0 comments on commit 97b0180

Please sign in to comment.