Newer
Older
import numpy as np
from .Ordinate import Ordinate
from .Absolute import Absolute
from .MeasurementType import MeasurementType as mt
"""
Class object for performing calculations.
Contains the following:
angle: average angle across a measurement type
residual: average residual across a measurement type
hs: Multiplier for inclination claculations. +1 if measurment was taken in northern hemisphere, -1 if measurement was taken in the southern hemishpere.
ordinate: Variometer data. Ordinate object(contains a datapoint for H, E, Z, and F)
ud: Multiplier for inclination calculations. +1 if instrument is oriented upward, -1 if instrument if oriented downward.
shift: Degree shift in inclination measurements.
"""
angle: float = None
residual: float = None
ordinate: Ordinate = None
f: float = None
ud: int = None
pm: int = None
shift: int = None
def calculate(reading):
"""
Calculate/recalculate absolute from a Reading object's
ordinates, measurements, and metadata.
Outputs a list of absolutes containing baseline, absolute,
and element name. Also reutrns the scale value.
"""
# gather oridinates, measuremenets, and metadata objects from reading
metadata = reading.metadata
ordinates = reading.ordinates
ordinate_index = reading.ordinate_index()
measurements = reading.measurements
measurement_index = reading.measurement_index()
# define measurement types used to calculate inclination
inclination_types = [mt.NORTH_DOWN, mt.NORTH_UP, mt.SOUTH_DOWN, mt.SOUTH_UP]
# get ordinate values across h, e, z, and f for inclination measurement types
Cain, Payton David
committed
inclination_ordinates = [
o for o in ordinates if o.measurement_type in inclination_types
Cain, Payton David
committed
]
# get average ordinate values across h, e, z, and f
Cain, Payton David
committed
mean = average_ordinate(inclination_ordinates, None)
# calculate inclination
inclination, f = calculate_I(
measurement_index, inclination_ordinates, ordinate_index, mean, metadata,
)
# calculate absolutes
h_abs, z_abs = calculate_absolutes(f, inclination)
h_b, z_b = calculate_baselines(h_abs, z_abs, mean, reading.pier_correction)
scale_ordinates = ordinate_index[mt.NORTH_DOWN_SCALE]
scale_measurements = measurement_index[mt.NORTH_DOWN_SCALE]
scale = calculate_scale(f, scale_ordinates, scale_measurements, inclination,)
# calculate declination absolute and baseline
d_b, d_abs = calculate_D(
ordinate_index, measurements, measurement_index, metadata["mark_azimuth"], h_b,
)
# return results as a set of Absolute objects along with the calculated scale value
resultH = Absolute(element="H", baseline=h_b, absolute=h_abs)
resultD = Absolute(element="D", baseline=d_b, absolute=d_abs)
resultZ = Absolute(element="Z", baseline=z_b, absolute=z_abs)
result = [resultH, resultD, resultZ]
return result, scale
def calculate_I(measurements, ordinates, ordinates_index, mean, metadata):
Cain, Payton David
committed
"""
Calculate inclination angles from measurements, ordinates,
average ordinates from every measurement, and metadata.
Returns inclination angle and calculated average f
"""
# get first inclination angle, assumed to be the southdown angle
Iprime = average_angle(measurements, mt.SOUTH_DOWN)
Cain, Payton David
committed
# get multiplier for hempisphere the observatory is located in
# 1 if observatory is in northern hemisphere
# -1 if observatory is in southern hemisphere
hs = metadata["hemisphere"]
# gather calculation objects for each measurement type
southdown = Calculate(
angle=average_angle(measurements, mt.SOUTH_DOWN),
residual=average_residual(measurements, mt.SOUTH_DOWN),
ordinate=average_ordinate(ordinates_index, mt.SOUTH_DOWN),
southup = Calculate(
angle=average_angle(measurements, mt.SOUTH_UP),
residual=average_residual(measurements, mt.SOUTH_UP),
ordinate=average_ordinate(ordinates_index, mt.SOUTH_UP),
northup = Calculate(
Cain, Payton David
committed
shift=0,
angle=average_angle(measurements, mt.NORTH_UP),
residual=average_residual(measurements, mt.NORTH_UP),
ordinate=average_ordinate(ordinates_index, mt.NORTH_UP),
)
northdown = Calculate(
angle=average_angle(measurements, mt.NORTH_DOWN),
residual=average_residual(measurements, mt.NORTH_DOWN),
ordinate=average_ordinate(ordinates_index, mt.NORTH_DOWN),
)
# set inclination value for looping = Iprime
# add one to inclination value to enter the loop
Cain, Payton David
committed
Inclination = inclination + 1
while abs(Inclination - inclination) > 0.0001:
# set temporary inclination variable to hold previous step's inclination
Cain, Payton David
committed
Inclination = inclination
# calculate f for inclination measurement types
southdown.f = calculate_f(southdown.ordinate, mean, inclination)
northdown.f = calculate_f(northdown.ordinate, mean, inclination)
southup.f = calculate_f(southup.ordinate, mean, inclination)
northup.f = calculate_f(northup.ordinate, mean, inclination)
# gather measurements into array(necessary because f were recalculated)
measurements = [southup, southdown, northup, northdown]
# average f values for inclination measurement types
f = np.average([i.f for i in measurements])
# calculation inclination for each inclination measurement type and average
inclination = np.average(
[calculate_measurement_inclination(m, hs) for m in measurements]
)
# loop exits once the difference in average inclination between steps is lower than 0.0001
def calculate_D(ordinates_index, measurements, measurements_index, azimuth, h_b):
Cain, Payton David
committed
"""
Calculate declination absolute and declination baseline from
ordinates, measurements, measurement_index(dictionary), azimuth and H baseline
Returns absolute and baseline for declination.
"""
# specify mark measurement types
mark_types = [
mt.FIRST_MARK_DOWN,
mt.FIRST_MARK_UP,
mt.SECOND_MARK_DOWN,
mt.SECOND_MARK_UP,
]
# average mark angles
# note that angles are being converted to geon
average_mark = np.average(
[
convert_to_geon(m.angle)
for m in measurements
)
# add 100 if mark up is greater than mark down
# subtract 100 otherwise
if (
measurements_index["FirstMarkUp"][0].angle
< measurements_index["FirstMarkDown"][0].angle
):
average_mark += 100
else:
average_mark -= 100
# gather calculation objects for each declination measurement type
# note that the pm(plus minus) multiplier has been repurposed.
# West facing measurements have a multiplier of -1
# East facing measurements have a multipllier of 1
westdown = Calculate(
angle=average_angle(measurements_index, mt.WEST_DOWN),
residual=average_residual(measurements_index, mt.WEST_DOWN),
ordinate=average_ordinate(ordinates_index, mt.WEST_DOWN),
Cain, Payton David
committed
)
westup = Calculate(
angle=average_angle(measurements_index, mt.WEST_UP),
residual=average_residual(measurements_index, mt.WEST_UP),
ordinate=average_ordinate(ordinates_index, mt.WEST_UP),
Cain, Payton David
committed
)
eastdown = Calculate(
angle=average_angle(measurements_index, mt.EAST_DOWN),
residual=average_residual(measurements_index, mt.EAST_DOWN),
ordinate=average_ordinate(ordinates_index, mt.EAST_DOWN),
Cain, Payton David
committed
)
eastup = Calculate(
angle=average_angle(measurements_index, mt.EAST_UP),
residual=average_residual(measurements_index, mt.EAST_UP),
ordinate=average_ordinate(ordinates_index, mt.EAST_UP),
Cain, Payton David
committed
)
azimuth = (int(azimuth / 100) + (azimuth % 100) / 60) / 0.9
# gather declination measurements into array
measurements = [westdown, westup, eastdown, eastup]
# average meridian terms calculated from each declination measurements
meridian = np.average([calculate_meridian_term(i, h_b) for i in measurements])
# add subtract average mark angle from average meridian angle and add
# azimuth(in geon) to get the declination baseline
# convert decimal baseline into dms baseline
d_b = round(D_b * 54, 2)
d_b_dms = int(d_b / 60) * 100 + ((d_b / 60) % 1) * 60
# gather first declination measurements' H ans E ordinates
wd_E_1 = ordinates_index[mt.WEST_DOWN][0].e
wd_H_1 = ordinates_index[mt.WEST_DOWN][0].h
d_abs = D_b + np.arctan(wd_E_1 / (h_b + wd_H_1)) * (200 / np.pi)
d_abs = round(d_abs * 54, 1)
# convert decimal absolute into dms absolute
d_abs_dms = int(d_abs / 60) * 100 + ((d_abs / 60) % 1) * 60
Cain, Payton David
committed
"""
Calculate absolutes for H, Z and F from computed
average f value(from inclination computations) and
calculated inclination angle.
Returns baselines for H and Z
Cain, Payton David
committed
"""
i = (np.pi / 200) * (inclination)
h_abs = f * np.cos(i)
z_abs = f * np.sin(i)
def calculate_baselines(h_abs, z_abs, mean, pier_correction):
Cain, Payton David
committed
"""
Calculate baselines with H and Z absolutes,
average ordinates across all measurements,
and pier correction(metadata).
Cain, Payton David
committed
Returns H and Z baselines
"""
correction = pier_correction / 5
h_b = round(np.sqrt(h_abs ** 2 - mean.e ** 2) - mean.h, 1) - correction
z_b = round(z_abs - mean.z, 1) - correction
def calculate_scale(f, ordinates, measurements, inclination):
Cain, Payton David
committed
"""
Calculate scale value from calulated f(from inclination computations),
inclination, and the measurements/ordinates taken for scaling purposes.
Such measurements usually present themselves as a set of three North Down
measurements, where the final two measuremets(and ordinates) are used for scaling.
Cain, Payton David
committed
"""
first_ord = ordinates[0]
second_ord = ordinates[1]
first_measurement = measurements[0]
second_measurement = measurements[1]
field_change = (
(
-np.sin(inclination * np.pi / 200) * (second_ord.h - first_ord.h) / f
+ np.cos(inclination * np.pi / 200) * (second_ord.z - first_ord.z) / f
)
* 200
/ np.pi
)
field_change += 0.1852
residual_change = abs(second_measurement.residual - first_measurement.residual)
scale_value = (f * field_change / residual_change) * np.pi / 200
return scale_value
def average_angle(measurements, type):
Cain, Payton David
committed
"""
Compute average angle from a dictionary of
measurements and specified measurement type.
"""
return np.average([convert_to_geon(m.angle) for m in measurements[type]])
def average_residual(measurements, type):
Cain, Payton David
committed
"""
Compute average residual from a dictionary
of measurements and specified measurement type.
"""
return np.average([m.residual for m in measurements[type]])
Cain, Payton David
committed
"""
Compute average ordinate from a dictionary
of ordinates and specified measurement type.
"""
if type is not None:
o = Ordinate(measurement_type=type)
avgs = np.average([[o.h, o.e, o.z, o.f] for o in ordinates], axis=0,)
o.h, o.e, o.z, o.f = avgs
return o
def calculate_f(ordinate, mean, inclination):
Cain, Payton David
committed
"""
calculate f for a measurement type using a measurement's
average ordinates, average ordinate across all measurements,
and calculated inclination.
"""
# get channel means form all ordinates
# calculate f using current step's inclination angle
f = (
mean.f
+ (ordinate.h - mean.h) * np.cos(inclination * np.pi / 200)
+ (ordinate.z - mean.z) * np.sin(inclination * np.pi / 200)
+ ((ordinate.e) ** 2 - (mean.e) ** 2) / (2 * mean.f)
)
return f
def calculate_measurement_inclination(calculation, hs):
Cain, Payton David
committed
"""
Calculate a measurement's inclination value using
Calculate items' elements.
"""
return calculation.shift + calculation.pm * (
+calculation.angle
+ calculation.ud
* (hs * np.arcsin(calculation.residual / calculation.f) * 200 / np.pi)
Cain, Payton David
committed
def calculate_meridian_term(calculation, h_b):
Cain, Payton David
committed
"""
Calculate meridian value from a measurement type
using a Calculate object and H's baseline value.
"""
calculation.residual
/ np.sqrt((calculation.ordinate.h + h_b) ** 2 + (calculation.ordinate.e) ** 2)
A2 = np.arctan(calculation.ordinate.e / (calculation.ordinate.h + h_b))
A1 = (200 / np.pi) * (A1)
A2 = (200 / np.pi) * (A2)
meridian_term = calculation.angle + (calculation.pm * A1) - A2
return meridian_term
def convert_to_geon(angle, include_seconds=True):
"""
Convert angles from measurements to geon
"""
degrees = int(angle)
minutes = int((angle % 1) * 100) / 60
if include_seconds:
seconds = ((angle * 100) % 1) / 36
else:
seconds = 0
dms = (degrees + minutes + seconds) / 0.9