Source code for eaarl.project

# -*- coding: utf-8 -*-
# vim: set fileencoding=utf-8 :
'''Geometry for mirror and target projection'''

# Boilerplate for cross-compatibility of Python 2/3
from __future__ import unicode_literals
from __future__ import print_function
from __future__ import division
from __future__ import absolute_import
from future.builtins import * # pylint: disable=wildcard-import
import future.standard_library
future.standard_library.install_aliases()

import math

import numpy as np
from scipy.constants import nano
from .constants import speed_of_light_air

from .io.waveforms._util import scan_counts_to_degrees

[docs]def target_range(base_range, tx_pos, rx_pos, channel, ops): '''Calculate range from mirror to target Parameters base_range : array-like of integer Nanoseconds from start of transmit waveform to return waveform (i.e., frame.range) tx_pos : array-like of float Position in transmit of center of energy rx_pos : array-like of float Position in return of target center of energy channel : array-like of integer Channel of point ops : dict The ops conf data from the flight Returns : float Distance from mirror to target, in meters. ''' range_bias = np.array( [ops['chn{0}_range_bias'.format(chan)] for chan in channel]) samples = base_range + rx_pos - tx_pos + range_bias distance = samples * nano * speed_of_light_air * 0.5 return distance - ops['range_biasM']
[docs]def project_mirror(frame, ops): '''Calculate mirror position Adds the mirror position to the frame DataFrame as mir_x, mir_y, and mir_z. Parameters frame : pandas.DataFrame A DataFrame containing the pulse records. ops : dict The ops conf data from the flight ''' R_ar = _frame_rotation_aircraft(frame, ops) s_gm = np.array([ops['x_offset'], ops['y_offset'], ops['z_offset']]) p_g = np.array([frame.ins_east, frame.ins_north, frame.ins_alt]).T p_m = _calc_mirror(R_ar, s_gm, p_g) frame['mir_x'] = p_m[:, 0] frame['mir_y'] = p_m[:, 1] frame['mir_z'] = p_m[:, 2]
[docs]def project_point(frame, ops, r_mt, coord_prefix=''): '''Calculate the target point position Adds the target position to the frame DataFrame as x, y, and z, with the prefix prepended if provided. Parameters frame : pandas.DataFrame A DataFrame containing the pulse records. ops : dict The ops conf data from the flight r_mt : array-like of float Distance from mirror to target, in meters coord_prefix : string A prefix to prepend to x, y, and z when adding the target coordinates to the DataFrame. The prefix will have _ added if missing. For example, if ``coord_prefix='fs'``, then the target coordinates will be added as 'fs_x', 'fs_y', and 'fs_z'. ''' R_ar = _frame_rotation_aircraft(frame, ops) p_m = np.array([frame.mir_x, frame.mir_y, frame.mir_z]).T broadcast = lambda val: np.array(np.broadcast_to(val, frame.mir_x.shape)) t_la_z = broadcast(0.) t_la_x = broadcast(45. - 0.4) t_ma_z = broadcast(0.) t_ma_x = broadcast(-22.5) t_ma_y = broadcast(frame.scan_angle + scan_counts_to_degrees(ops['scan_bias'])) t_ma_y, t_la_x = _spacing(t_ma_y, t_la_x, frame.channel, ops) dv_i = _calc_incidence(R_ar, t_la_z, t_la_x) dv_n = _calc_normal(R_ar, t_ma_z, t_ma_x, t_ma_y) del R_ar dv_s = _calc_reflection(dv_i, dv_n) del dv_i, dv_n p_t = _calc_target(dv_s, r_mt, p_m) if coord_prefix and coord_prefix[-1:] != '_': coord_prefix += '_' frame[coord_prefix + 'x'] = p_t[:, 0] frame[coord_prefix + 'y'] = p_t[:, 1] frame[coord_prefix + 'z'] = p_t[:, 2]
def _spacing(t_ma_y, t_la_x, channels, ops): '''Adjusts the angles for the EAARL-B target spacing Returns the adjusted (t_ma_y, t_la_x). The angles are adjusted to account for the EAARL-B target spacing. Parameters t_ma_y : array-like of float Array of mirror y-axis rotations, in degrees t_la_x : array-like of float Array of laser x-axis rotations, in degrees channels : array-like of integer Channel of point ops : dict The ops conf data from the flight ''' if 'delta_ht' not in ops: return t_ma_y, t_la_x dz = ops['delta_ht'] for channel in np.unique(channels): where = np.array(int(channel) == channels) dx = ops['chn{0}_dx'.format(channel)] dy = ops['chn{0}_dy'.format(channel)] if dx != 0 and dz != 0: tx = math.degrees(math.atan2(dx, dz)) t_ma_y[where] -= tx if dy != 0 and dz != 0: ty = math.degrees(math.atan2(dy, dz)) t_la_x[where] -= ty return t_ma_y, t_la_x def _frame_rotation_aircraft(frame, ops): '''Returns rotation matrix for the aircraft''' t_ar_z = -frame.ins_heading + ops['yaw_bias'] t_ar_x = frame.ins_pitch + ops['pitch_bias'] t_ar_y = frame.ins_roll + ops['roll_bias'] return _rotation_aircraft(t_ar_z, t_ar_x, t_ar_y) def _rotation_aircraft(t_ar_z, t_ar_x, t_ar_y): '''Returns rotation matrx for the aircraft''' t_ar_x_rad = np.deg2rad(t_ar_x) cos_x = np.cos(t_ar_x_rad) sin_x = np.sin(t_ar_x_rad) del t_ar_x_rad t_ar_y_rad = np.deg2rad(t_ar_y) cos_y = np.cos(t_ar_y_rad) sin_y = np.sin(t_ar_y_rad) del t_ar_y_rad t_ar_z_rad = np.deg2rad(t_ar_z) cos_z = np.cos(t_ar_z_rad) sin_z = np.sin(t_ar_z_rad) del t_ar_z_rad sin_x_sin_y = sin_x * sin_y cos_y_cos_z = cos_y * cos_z cos_y_sin_z = cos_y * sin_z R_ar = np.zeros(cos_x.shape + (3, 3)) R_ar[..., 0, 0] = cos_y_cos_z - sin_z * sin_x_sin_y R_ar[..., 0, 1] = - sin_z * cos_x R_ar[..., 0, 2] = cos_z * sin_y + cos_y_sin_z * sin_x R_ar[..., 1, 0] = cos_y_sin_z + cos_z * sin_x_sin_y R_ar[..., 1, 1] = cos_x * cos_z R_ar[..., 1, 2] = sin_y * sin_z - cos_y_cos_z * sin_x R_ar[..., 2, 0] = - cos_x * sin_y R_ar[..., 2, 1] = sin_x R_ar[..., 2, 2] = cos_x * cos_y return R_ar def _calc_mirror(R_ar, s_gm, p_g): '''Calculates the mirror position''' p_m = np.zeros(p_g.shape) p_m[..., 0] = ( p_g[..., 0] + R_ar[..., 0, 0] * s_gm[..., 0] + R_ar[..., 0, 1] * s_gm[..., 1] + R_ar[..., 0, 2] * s_gm[..., 2] ) p_m[..., 1] = ( p_g[..., 1] + R_ar[..., 1, 0] * s_gm[..., 0] + R_ar[..., 1, 1] * s_gm[..., 1] + R_ar[..., 1, 2] * s_gm[..., 2] ) p_m[..., 2] = ( p_g[..., 2] + R_ar[..., 2, 0] * s_gm[..., 0] + R_ar[..., 2, 1] * s_gm[..., 1] + R_ar[..., 2, 2] * s_gm[..., 2] ) return p_m def _calc_incidence(R_ar, t_la_z, t_la_x): '''Calculates the laser angle of incidence''' sin = lambda theta: np.sin(theta)[..., np.newaxis] cos = lambda theta: np.cos(theta)[..., np.newaxis] x = np.deg2rad(t_la_x) z = np.deg2rad(t_la_z) dv_i = ( R_ar[..., 0] * sin(z) - R_ar[..., 1] * cos(z) ) dv_i *= cos(x) dv_i -= R_ar[..., 2] * sin(x) return dv_i def _calc_normal(R_ar, t_ma_z, t_ma_x, t_ma_y): '''Calculates the laser normal angle''' t_ma_x_rad = np.deg2rad(np.broadcast_to(np.array(t_ma_x), R_ar.shape[:-2])) cos_x = np.cos(t_ma_x_rad) sin_x = np.sin(t_ma_x_rad) del t_ma_x_rad t_ma_y_rad = np.deg2rad(np.broadcast_to(np.array(t_ma_y), R_ar.shape[:-2])) cos_y = np.cos(t_ma_y_rad) sin_y = np.sin(t_ma_y_rad) del t_ma_y_rad t_ma_z_rad = np.deg2rad(np.broadcast_to(np.array(t_ma_z), R_ar.shape[:-2])) cos_z = np.cos(t_ma_z_rad) sin_z = np.sin(t_ma_z_rad) del t_ma_z_rad sin_x_cos_y = sin_x * cos_y dv_n = R_ar[..., 0] * ( (sin_y * cos_z) + (sin_x_cos_y * sin_z) )[:, np.newaxis] dv_n += R_ar[..., 1] * ( sin_y * sin_z - sin_x_cos_y * cos_z )[:, np.newaxis] dv_n += R_ar[..., 2] * (cos_x * cos_y)[:, np.newaxis] return dv_n def _calc_reflection(dv_i, dv_n): '''Calculates the laser reflection angle''' return 2 * np.sum(dv_i * dv_n, axis=-1, keepdims=True) * dv_n - dv_i def _calc_target(dv_s, r_mt, p_m): '''Calculates the target position''' return dv_s * np.array(r_mt)[..., np.newaxis] + p_m