Newer
Older

Jeremy M Fee
committed
from typing import List, Literal, Tuple
import numpy as np
from .Absolute import Absolute
from .MeasurementType import (
MeasurementType as mt,
DECLINATION_TYPES,
INCLINATION_TYPES,
MARK_TYPES,
)
from .Measurement import AverageMeasurement, Measurement, average_measurement
from .Diagnostics import Diagnostics
def calculate(reading: Reading, adjust_reference: bool = True) -> Reading:
"""Calculate absolutes and scale value using residual method.
Parameters
-------
reading: reading to calculate absolutes from.
Returns
-------
new reading object with calculated absolutes and scale_value.
NOTE: rest of reading object is shallow copy.
# reference measurement, used to adjust absolutes
missing_types = reading.get_missing_measurement_types()
if len(missing_types) != 0:
missing_types = ", ".join(t.value for t in missing_types)
raise ValueError(f"Missing {missing_types} measurements in input reading")
reference = adjust_reference and reading[mt.WEST_DOWN][0] or None
# calculate inclination
inclination, f, i_mean = calculate_I(
hemisphere=reading.hemisphere, measurements=reading.measurements
)
corrected_f = f + reading.pier_correction
# calculate absolutes, horizontal_component, vertical_component
absoluteH, absoluteZ, horizontal_component, vertical_component = calculate_HZ_absolutes(
corrected_f=corrected_f,
inclination=inclination,
reference=reference,
absoluteD, meridian = calculate_D_absolute(
azimuth=reading.azimuth,
h_baseline=absoluteH.baseline,
measurements=reading.measurements,
reference=reference,
d_computed=calculate_D_computed(
measurements=reading.measurements,
h_baseline=absoluteH.baseline,
r_computed=calculate_R_computed(
measurements=reading.measurements,
h_baseline=absoluteH.baseline,
)
magnetic_south_meridian=calculate_magnetic_south_meridian(
measurements=reading.measurements,
)
scale_value = None
scale_measurements = reading[mt.NORTH_DOWN_SCALE]
if scale_measurements:
scale_value = calculate_scale_value(
corrected_f=corrected_f,
inclination=inclination,
measurements=scale_measurements,
mean_mark = calculate_mean_mark(
measurements=reading.measurements,
)
magnetic_azimuth_of_mark=calculate_magnetic_azimuth_of_mark(
mean_mark=mean_mark,
magnetic_south_meridian=magnetic_south_meridian
)
ordinate_f=calculate_ordinate_f(
corrected_f=corrected_f,
pier_correction=reading.pier_correction,
)
ordinate_d=calculate_ordinate_d(
absoluteD_absolute=absoluteD.absolute,
absoluteD_baseline=absoluteD.baseline,
)
ordinate_h=calculate_ordinate_h(
absoluteH_absolute=absoluteH.absolute,
absoluteH_baseline=absoluteH.baseline,
ordinate_d=ordinate_d,
)
ordinate_z=calculate_ordinate_z(
absoluteZ_absolute=absoluteZ.absolute,
absoluteZ_baseline=absoluteZ.baseline,
)
# populate diagnostics object with averaged measurements
diagnostics = Diagnostics(
corrected_f=corrected_f,
d_computed=d_computed,
horizontal_component=horizontal_component,
magnetic_azimuth_of_mark=magnetic_azimuth_of_mark,
magnetic_south_meridian=magnetic_south_meridian,
mean_mark=mean_mark,
ordinate_d=ordinate_d,
ordinate_f=ordinate_f,
ordinate_h=ordinate_h,
ordinate_z=ordinate_z,
r_computed=r_computed,
scale_value=scale_value,
vertical_component=vertical_component,
# create new reading object
calculated = Reading(
absolutes=[absoluteD, absoluteH, absoluteZ],
diagnostics=diagnostics,
**reading.dict(exclude={"absolutes", "diagnostics"}),
def calculate_D_absolute(
measurements: List[Measurement],
azimuth: float,
h_baseline: float,
reference: Measurement,
) -> Tuple[Absolute, Diagnostics]:
Parameters
----------
measurements: list with at least declination and mark measurements.
azimuth: azimuth to mark in decimal degrees.
h_baseline: calculated H baseline value.
reference: reference measurement used to adjust absolute.
Returns
-------
D Absolute
Cain, Payton David
committed
"""
mean = average_measurement(measurements, DECLINATION_TYPES)
if reference:
starttime = reference.time
endtime = reference.time
else:
starttime = mean.time
endtime = mean.endtime
reference = reference or mean
average_mark = average_measurement(measurements, MARK_TYPES)
# adjust based on which is larger
mark_up = average_measurement(measurements, [mt.FIRST_MARK_UP]).angle
mark_down = average_measurement(measurements, [mt.FIRST_MARK_DOWN]).angle
if mark_up < mark_down:
average_mark.angle += 90
average_mark.angle -= 90
# declination measurements
declination_measurements = [
average_measurement(measurements, [t]) for t in DECLINATION_TYPES
]
# average declination meridian
meridian = np.average(
[
m.angle
+ np.degrees(
m.measurement_type.meridian

Jeremy M Fee
committed
* (np.arcsin(m.residual / np.sqrt((m.h + h_baseline) ** 2 + m.e**2)))
)
- np.degrees(np.arctan(m.e / (m.h + h_baseline)))
for m in declination_measurements
]
)
if azimuth > 180:
# add subtract average mark angle from average meridian angle and add
# azimuth to get the declination baseline
d_b = (meridian - average_mark.angle) + azimuth + shift
d_abs = d_b + np.degrees(np.arctan(reference.e / (reference.h + h_baseline)))
return (
Absolute(
element="D",
absolute=d_abs,
baseline=d_b,
shift=shift,
starttime=starttime,
endtime=endtime,
def calculate_HZ_absolutes(
inclination: float,
corrected_f: float,
mean: AverageMeasurement,
reference: Measurement,
) -> Tuple[Absolute, Absolute, float, float]:
"""Calculate H and Z absolutes, horizontal_component, vertical_component.
Parameters
----------
inclination: calculated inclination.
corrected_f: calculated f with pier correction.
mean: mean of inclination ordinates.
reference: reference measurement used to adjust absolutes.
Returns
-------
Tuple
- H Absolute
- Z Absolute
- horizontal_component,
- vertical_component
# store the pre-shifted h_abs and the pre-shifted z_abs
inclination_radians = np.radians(inclination)
horizontal_component=corrected_f * np.cos(inclination_radians)
vertical_component=corrected_f * np.sin(inclination_radians)
h_abs = corrected_f * np.cos(inclination_radians)
z_abs = corrected_f * np.sin(inclination_radians)

Jeremy M Fee
committed
h_b = np.sqrt(h_abs**2 - mean.e**2) - mean.h
# adjust absolutes to reference measurement
h_abs = np.sqrt((h_b + reference.h) ** 2 + (reference.e) ** 2)
z_abs = z_b + reference.z
starttime = reference.time
endtime = reference.time
else:
starttime = mean.time
endtime = mean.endtime
# return absolutes
return (
Absolute(
element="H",
baseline=h_b,
absolute=h_abs,
starttime=starttime,
endtime=endtime,
),
Absolute(
element="Z",
baseline=z_b,
absolute=z_abs,
starttime=starttime,
endtime=endtime,
horizontal_component,
vertical_component
)
def calculate_I(
measurements: List[Measurement], hemisphere: Literal[-1, 1] = 1
) -> Tuple[float, float, Measurement]:
"""Calculate inclination and f from measurements.
Parameters
----------
measurements: list with at least inclination measurements.
hemisphere: +1 for northern hemisphere (default), -1 for southern hemisphere.
Returns
-------
Tuple
- inclination angle in decimal degrees
- uncorrected calculated f
- mean of inclination measurements
Cain, Payton David
committed
"""
mean = average_measurement(measurements, INCLINATION_TYPES)
inclination_measurements = [
average_measurement(measurements, [t]) for t in INCLINATION_TYPES
]
# get initial inclination angle, assumed to be the southdown angle
inclination = average_measurement(measurements, [mt.SOUTH_DOWN]).angle
if inclination >= 90:
inclination -= 180
# loop until inclination converges
last_inclination = inclination + 1
while abs(last_inclination - inclination) > 0.0001:
# set temporary inclination variable to hold previous step's inclination
last_inclination = inclination
# Update measurement f based on inclination
inclination_radians = np.radians(inclination)
m.f = (
mean.f
+ (m.h - mean.h) * np.cos(inclination_radians)
+ (m.z - mean.z) * np.sin(inclination_radians)
+ ((m.e) ** 2 - (mean.e) ** 2) / (2 * mean.f)
(
m.measurement_type.shift
+ m.measurement_type.meridian
* (
+m.angle
+ m.measurement_type.direction
* (hemisphere * np.degrees(np.arcsin(m.residual / m.f)))
# calculate uncorrected f
f = np.average([m.f for m in inclination_measurements])
def calculate_scale_value(
measurements: List[Measurement], inclination: float, corrected_f: float
) -> float:
"""Calculate scale value.
Parameters
----------
measurements: measurements to be used for scale calculation.
should have type NORTH_DOWN_SCALE.
inclination: calculated inclination.
corrected_f: calculated f with pier correction.
Returns
-------
Calculated scale value.
Cain, Payton David
committed
"""
m1, m2 = measurements[0], measurements[-1]
field_change = np.degrees(
(
-np.sin(inclination_radians) * (m2.h - m1.h)
+ np.cos(inclination_radians) * (m2.z - m1.z)
/ corrected_f
) + (m2.angle - m1.angle)
residual_change = m2.residual - m1.residual
scale_value = corrected_f * field_change / np.abs(residual_change)
return scale_value
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
def calculate_magnetic_south_meridian(
measurements: List[Measurement]) -> float:
"""Calculate magnetic_south_meridian.
Parameters
----------
measurements: measurements, should have type WEST_DOWN, EAST_DOWN, WEST_UP, EAST_UP.
Returns
-------
Calculated magnetic_south_meridian.
"""
# declination measurements
declination_measurements = [
average_measurement(measurements, [t]) for t in DECLINATION_TYPES
]
magnetic_south_meridian = np.average(
[
m.angle
for m in declination_measurements
]
)
return magnetic_south_meridian
def calculate_D_computed(
measurements: List[Measurement],
h_baseline: float,) -> float:
"""Calculate d_computed.
Parameters
----------
measurements: measurements,
h_baseline
Returns
-------
Calculated d_computed.
"""
# declination measurements
declination_measurements = [
average_measurement(measurements, [t]) for t in DECLINATION_TYPES
]
d_computed = np.average(
[np.degrees(np.arctan(m.e / (m.h + h_baseline)))
for m in declination_measurements
]
)
return d_computed
def calculate_R_computed(
measurements: List[Measurement],
h_baseline: float,) -> float:
"""Calculate r_computed.
Parameters
----------
measurements: measurements,
h_baseline
Returns
-------
Calculated r_computed.
"""
# declination measurements
declination_measurements = [
average_measurement(measurements, [t]) for t in DECLINATION_TYPES
]
r_computed = np.average(
[ np.degrees(
m.measurement_type.meridian
* (np.arcsin(m.residual / np.sqrt((m.h + h_baseline) ** 2 + m.e**2)))
)
for m in declination_measurements
]
)
return r_computed
def calculate_mean_mark(
measurements: List[Measurement],
) -> float:
"""Calculate mean_mark.
Parameters
----------
measurements: measurements,
Returns
-------
Calculated mean_mark angle.
"""
# average mark
average_mark = average_measurement(measurements, MARK_TYPES)
return average_mark.angle
def calculate_magnetic_azimuth_of_mark(
mean_mark: float,
magnetic_south_meridian:float,
) -> float:
"""Calculate magnetic_azimuth_of_mark.
Parameters
----------
mean_mark: float,
magnetic_south_meridian:float,
Returns
-------
Calculated magnetic_azimuth_of_mark
"""
magnetic_azimuth_of_mark = mean_mark - magnetic_south_meridian
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
return magnetic_azimuth_of_mark
def calculate_ordinate_f(
corrected_f: float,
pier_correction:float,
) -> float:
"""Calculate ordinate_f.
Parameters
----------
corrected_f: float,
pier_correction:float,
Returns
-------
Calculated ordinate_f
"""
ordinate_f = corrected_f - pier_correction
return ordinate_f
def calculate_ordinate_d(
absoluteD_absolute: float,
absoluteD_baseline:float,
) -> float:
"""Calculate ordinate_d.
Parameters
----------
absoluteD_absolute: float,
absoluteD_baseline:float,
Returns
-------
Calculated ordinate_d
"""
ordinate_d = absoluteD_absolute - absoluteD_baseline
return ordinate_d
def calculate_ordinate_z(
absoluteZ_absolute: float,
absoluteZ_baseline:float,
) -> float:
"""Calculate ordinate_z.
Parameters
----------
absoluteZ_absolute: float,
absoluteZ_baseline:float,
Returns
-------
Calculated ordinate_z
"""
ordinate_z = absoluteZ_absolute - absoluteZ_baseline
return ordinate_z
def calculate_ordinate_h(
absoluteH_absolute: float,
absoluteH_baseline:float,
ordinate_d:float,
) -> float:
"""Calculate ordinate_z.
Parameters
----------
absoluteH_absolute: float,
absoluteH_baseline:float,
ordinate_d:float,
Returns
-------
Calculated ordinate_h
"""
ordinate_e = absoluteH_absolute * np.sin(ordinate_d)
ordinate_h = np.sqrt(absoluteH_absolute**2 - ordinate_e**2) - absoluteH_baseline
return ordinate_h