diff --git a/bin/geomag.py b/bin/geomag.py index 67c34d75b4dc5cf1c77f95659b8784b481f3e4b1..46a1689d3015b01aa87c282049fc3d85f8cbaf47 100755 --- a/bin/geomag.py +++ b/bin/geomag.py @@ -17,6 +17,7 @@ import geomagio.pcdcp as pcdcp from geomagio import Algorithm, \ Controller from geomagio.XYZAlgorithm import XYZAlgorithm +from geomagio.DeltaFAlgorithm import DeltaFAlgorithm from obspy.core import UTCDateTime @@ -142,6 +143,8 @@ def main(): if args.xyz is not None: algorithm = XYZAlgorithm(informat=args.xyz[0], outformat=args.xyz[1]) + elif args.deltaf is not None: + algorithm = DeltaFAlgorithm(informat=args.deltaf) else: # TODO get smarter on inchannels/outchannels since input doesn't always # need to use the --inchannels argument, but might (as in iaga2002), @@ -277,6 +280,9 @@ def parse_args(): choices=['geo', 'mag', 'obs', 'obsd'], help='Enter the geomagnetic orientation(s) you want to read from' + ' and to respectfully.') + algorithm_group.add_argument('--deltaf', + choices=['geo', 'obs', 'obsd'], + help='Enter the geomagnetic orientation you want to read from') return parser.parse_args() diff --git a/geomagio/ChannelConverter.py b/geomagio/ChannelConverter.py index 8181e42cd2c96f78d8eda7e6703450f777ceb398..1058f6c9c059f3a66361277fa01faa68c23220ea 100644 --- a/geomagio/ChannelConverter.py +++ b/geomagio/ChannelConverter.py @@ -378,6 +378,44 @@ def get_obs_h_from_mag(h, d, d0=0): return numpy.multiply(h, numpy.cos(obs_d)) +def get_deltaf(fv, fs): + """gets the deltaf value given the scalar F and computed F values. + + Parameters + ---------- + fv: array_like + the F vector computed from the observatory instruments + fs: array_like + the measured F value + """ + return numpy.subtract(fv, fs) + + +def get_computed_f_using_squares(x, y, z): + """gets the computed f value + + Parameters + ---------- + x: array_like + the x component from the observatory + y: array_like + the y component from the observatory + z: array_like + the z component from the observatory + Notes + ----- + This works for geographic coordinates, or observatory coordinates. + ie x, y, z or h, e, z + We're using variables x,y,z to represent generic cartisian coordinates. + """ + x2 = numpy.square(x) + y2 = numpy.square(y) + z2 = numpy.square(z) + fv = numpy.add(x2, y2) + fv = numpy.add(fv, z2) + return numpy.sqrt(fv) + + def get_radians_from_minutes(m): """gets the radian value given the decimal value Parameters diff --git a/geomagio/ChannelConverter_test.py b/geomagio/ChannelConverter_test.py index 00fe4bb6e5f98beae82402d369ff52b2f0a1277a..0c33b4b04b6f5cec4ad957a58eb36b412f204694 100644 --- a/geomagio/ChannelConverter_test.py +++ b/geomagio/ChannelConverter_test.py @@ -45,6 +45,21 @@ class ChannelConverterTest: length to be cos(angle) and the opposite length to be sin(angle) """ + def test_get_computed_f_using_sqaures(self): + """geomag.ChannelConverterTest.test_get_computed_f_using_sqaures + + Computed f is the combination of the 3 vector components from + either the geographic coordinate system (X,Y,Z), or the observatory + coordinate system (h,e,z). + """ + h = 2 + e = 2 + z = 2 + fv = channel.get_computed_f_using_squares(h, e, z) + fs = math.sqrt(12) + assert_almost_equal(fv, fs, 8, 'Expect fv to almost equal sqrt(12)', + True) + def test_get_geo_from_obs(self): """geomag.ChannelConverterTest.test_get_geo_from_obs() diff --git a/geomagio/DeltaFAlgorithm.py b/geomagio/DeltaFAlgorithm.py new file mode 100644 index 0000000000000000000000000000000000000000..d0619dee63ae90534ec4034063c383219cc90947 --- /dev/null +++ b/geomagio/DeltaFAlgorithm.py @@ -0,0 +1,70 @@ +"""Algorithm that creates a deltaf + +""" + +from Algorithm import Algorithm +from AlgorithmException import AlgorithmException +import StreamConverter as StreamConverter + +# List of channels by geomagnetic observatory orientation. +# geo represents a geographic north/south orientation +# obs represents the sensor orientation aligned close to the mag orientation +# obsd is the same as obs, but with D(declination) instead of E (e/w vector) +CHANNELS = { + 'geo': ['X', 'Y', 'Z', 'F'], + 'obs': ['H', 'E', 'Z', 'F'], + 'obsd': ['H', 'D', 'Z', 'F'] +} + + +class DeltaFAlgorithm(Algorithm): + """Algorithm for getting Delta F. + + Parameters + ---------- + informat: str + the code that represents the incoming data form that the Algorithm + will be converting from. + """ + + def __init__(self, informat=None): + Algorithm.__init__(self, inchannels=CHANNELS[informat], + outchannels=['G']) + self.informat = informat + + def check_stream(self, timeseries): + """checks a stream to make certain all the required channels + exist. + + Parameters + ---------- + timeseries: obspy.core.Stream + stream to be checked. + """ + for channel in self._inchannels: + if len(timeseries.select(channel=channel)) == 0: + raise AlgorithmException( + 'Channel %s not found in input' % channel) + + def process(self, timeseries): + """converts a timeseries stream into a different coordinate system + + Parameters + ---------- + informat: string + indicates the input coordinate system. + + Returns + ------- + out_stream: obspy.core.Stream + new stream object containing the converted coordinates. + """ + self.check_stream(timeseries) + out_stream = None + + if self.informat == 'geo': + out_stream = StreamConverter.get_deltaf_from_geo(timeseries) + elif self.informat == 'obs' or self.informat == 'obsd': + out_stream = StreamConverter.get_deltaf_from_obs(timeseries) + + return out_stream diff --git a/geomagio/StreamConverter.py b/geomagio/StreamConverter.py index 199ad55bbb7d096b7ec53477838fc581e6563ba5..e352ebb53501564078e693e16cfd1f4e8eb40b88 100644 --- a/geomagio/StreamConverter.py +++ b/geomagio/StreamConverter.py @@ -54,6 +54,52 @@ def get_geo_from_obs(obs): return get_geo_from_mag(get_mag_from_obs(obs)) +def get_deltaf_from_geo(geo): + """Get deltaf given geographic coordinate values + + Parameters + ---------- + obs: obspy.core.Stream + stream containing the observatory components H, D or E, Z, and F. + + Returns + ------- + obspy.core.Stream + stream object containing delta f values + """ + x = geo.select(channel='X')[0] + y = geo.select(channel='Y')[0] + z = geo.select(channel='Z')[0] + fs = geo.select(channel='F')[0] + fv = ChannelConverter.get_computed_f_using_squares(x, y, z) + G = ChannelConverter.get_deltaf(fv, fs) + return obspy.core.Stream(( + __get_trace('G', x.stats, G), )) + + +def get_deltaf_from_obs(obs): + """Get deltaf given observatory coordinate values + + Parameters + ---------- + obs: obspy.core.Stream + stream containing the observatory components H, D or E, Z, and F. + + Returns + ------- + obspy.core.Stream + stream object containing delta f values + """ + h = obs.select(channel='H')[0] + z = obs.select(channel='Z')[0] + fs = obs.select(channel='F')[0] + e = __get_obs_e_from_obs(obs) + fv = ChannelConverter.get_computed_f_using_squares(h, e, z) + G = ChannelConverter.get_deltaf(fv, fs) + return obspy.core.Stream(( + __get_trace('G', h.stats, G), )) + + def get_mag_from_geo(geo): """Convert a stream to magnetic coordinate system. @@ -166,7 +212,7 @@ def get_obs_from_obs(obs, include_e=False, include_d=False): Parameters ---------- stream: obspy.core.Stream - stream containing the observatory components H, D of E, Z, and F. + stream containing the observatory components H, D or E, Z, and F. include_e: boolean whether to include the e component include_d: boolean diff --git a/geomagio/__init__.py b/geomagio/__init__.py index 551b00c5272c3087a932b4a42e31a59530b26485..74c7d9ea771a19bcb02cd7068c234e885425d0f8 100644 --- a/geomagio/__init__.py +++ b/geomagio/__init__.py @@ -18,6 +18,7 @@ __all__ = [ 'AlgorithmException', 'ChannelConverter', 'Controller', + 'DeltaFAlgorithm', 'StreamConverter', 'TimeseriesFactory', 'TimeseriesFactoryException', diff --git a/geomagio/edge/EdgeFactory.py b/geomagio/edge/EdgeFactory.py index 795eb2ebfda0ce4f5c73a059f05c760ef1c693b1..06305985d14d0a804695501bfcd56c252ce66a84 100644 --- a/geomagio/edge/EdgeFactory.py +++ b/geomagio/edge/EdgeFactory.py @@ -342,6 +342,8 @@ class EdgeFactory(TimeseriesFactory): edge_channel = edge_interval_code + 'VH' elif channel == 'Z': edge_channel = edge_interval_code + 'VZ' + elif channel == 'G': + edge_channel = edge_interval_code + 'SG' else: raise TimeseriesFactoryException( 'Unexpected channel code "%s"' % channel)