|
| 1 | +from pyxlma.coords import RadarCoordinateSystem, TangentPlaneCartesianSystem, GeographicSystem |
| 2 | +import numpy as np |
| 3 | + |
| 4 | + |
| 5 | +def rcs_to_tps(radar_latitude, radar_longitude, radar_altitude, radar_azimuth): |
| 6 | + """ |
| 7 | + Find the unit vector coordinates (east, north, up) of the plane of a radar RHI scan. |
| 8 | + |
| 9 | + Creates a azimuth, elevation, range and tangent plane cartesian system at the radar's latitude and longitude, |
| 10 | + and converts the RHI azimuth direction to the tangent plane coordinate system. |
| 11 | +
|
| 12 | + Parameters |
| 13 | + ---------- |
| 14 | + radar_latitude : `float` |
| 15 | + Latitude of the radar in degrees. |
| 16 | + radar_longitude : `float` |
| 17 | + Longitude of the radar in degrees. |
| 18 | + radar_altitude : `float` |
| 19 | + Altitude of the radar in meters. |
| 20 | + radar_azimuth : `float` |
| 21 | + Azimuth of the RHI scan in degrees. |
| 22 | +
|
| 23 | + Returns |
| 24 | + ---------- |
| 25 | + X : `numpy.ndarray` |
| 26 | + A 1x2 array representing the start and end points eastward component of the RHI scan. |
| 27 | + Y : `numpy.ndarray` |
| 28 | + A 1x2 array representing the start and end points northward component of the RHI scan. |
| 29 | + Z : `numpy.ndarray` |
| 30 | + A 1x2 array representing the start and end points upward component of the RHI scan. |
| 31 | + """ |
| 32 | + |
| 33 | + # Coordinates Systems |
| 34 | + rcs = RadarCoordinateSystem(radar_latitude, radar_longitude, radar_altitude) |
| 35 | + tps = TangentPlaneCartesianSystem(radar_latitude, radar_longitude, radar_altitude) |
| 36 | + |
| 37 | + # - Elevations, azimuth, range |
| 38 | + r = np.array([0, 1]) |
| 39 | + |
| 40 | + els = np.array([0]) |
| 41 | + els = np.tensordot(els, np.ones_like(r), axes=0) |
| 42 | + |
| 43 | + azi = np.array([radar_azimuth]) |
| 44 | + az = np.tensordot(azi, np.ones_like(r), axes=0) |
| 45 | + |
| 46 | + a, b, c = rcs.toECEF(r,az,els) |
| 47 | + abc = np.vstack((a,b,c)) |
| 48 | + # ECEF to TPS |
| 49 | + n = tps.toLocal(abc) |
| 50 | + X = n[0,:] |
| 51 | + Y = n[1,:] |
| 52 | + Z = n[2,:] |
| 53 | + |
| 54 | + X = np.reshape(X, (1, 2)) |
| 55 | + Y = np.reshape(Y, (1, 2)) |
| 56 | + Z = np.reshape(Z, (1, 2)) |
| 57 | + |
| 58 | + return X, Y, Z |
| 59 | + |
| 60 | + |
| 61 | +def geo_to_tps(lma_file, tps_latitude, tps_longitude, tps_altitude): |
| 62 | + """ |
| 63 | + Convert the latitude, longitude, and altitude of LMA VHF sources to x/y/z distances (in meters) from an arbitrary latitude, longitude, and altitude. |
| 64 | +
|
| 65 | + Creates a tangent plane cartesian system at the latitude, longitude, and altitude provided, and converts the LMA VHF sources to the tangent plane coordinate system. |
| 66 | +
|
| 67 | + Parameters |
| 68 | + ---------- |
| 69 | + lma_file : `xarray.Dataset` |
| 70 | + A pyxlma dataset containing latitude, longitude, and altitude of LMA VHF sources. |
| 71 | + tps_latitude : `float` |
| 72 | + Latitude of the tangent plane in degrees. |
| 73 | + tps_longitude : `float` |
| 74 | + Longitude of the tangent plane in degrees. |
| 75 | + tps_altitude : `float` |
| 76 | + Altitude of the tangent plane in meters. |
| 77 | + |
| 78 | + Returns |
| 79 | + ---------- |
| 80 | + Xlma : `numpy.ndarray` |
| 81 | + A 1xN array representing the eastward distance (in meters) of the tangent plane center to the LMA VHF sources. |
| 82 | + Ylma : `numpy.ndarray` |
| 83 | + A 1xN array representing the northward distance (in meters) of the tangent plane center to the LMA VHF sources. |
| 84 | + Zlma : `numpy.ndarray` |
| 85 | + A 1xN array representing the upward distance (in meters) of the tangent plane center to the LMA VHF sources. |
| 86 | + """ |
| 87 | + # GeographicSystem GEO - Lat, lon, alt |
| 88 | + geo = GeographicSystem() |
| 89 | + # Tangent Plane Cartesian System TPS - |
| 90 | + tps = TangentPlaneCartesianSystem(tps_latitude, tps_longitude, tps_altitude) |
| 91 | + |
| 92 | + # GEO to TPS |
| 93 | + |
| 94 | + d, e, h = geo.toECEF(lma_file.event_longitude.data, lma_file.event_latitude.data, lma_file.event_altitude.data) |
| 95 | + |
| 96 | + |
| 97 | + deh = np.vstack((d,e,h)) |
| 98 | + m = tps.toLocal(deh) |
| 99 | + |
| 100 | + Xlma = m[0] |
| 101 | + Ylma = m[1] |
| 102 | + Zlma = m[2] |
| 103 | + |
| 104 | + return Xlma,Ylma,Zlma |
| 105 | + |
| 106 | + |
| 107 | +def ortho_proj_lma(lma_file, radar_latitude, radar_longitude, radar_altitude, radar_azimuth): |
| 108 | + """ |
| 109 | + Convert the latitude, longitude, and altitude of LMA VHF sources to distance along, distance from, and height above the ground of a radar RHI scan. |
| 110 | + |
| 111 | + Creates tangent plane at the radar's location and converts the LMA VHF sources to the tangent plane coordinate system, then rotates the tangent plane in the direction of the scan azimuth. |
| 112 | +
|
| 113 | + |
| 114 | + Parameters |
| 115 | + ---------- |
| 116 | + lma_file : `xarray.Dataset` |
| 117 | + A pyxlma dataset containing latitude, longitude, and altitude of N number of LMA VHF sources. |
| 118 | + radar_latitude : `float` |
| 119 | + Latitude of the radar in degrees. |
| 120 | + radar_longitude : `float` |
| 121 | + Longitude of the radar in degrees. |
| 122 | + radar_altitude : `float` |
| 123 | + Altitude of the radar in meters. |
| 124 | + radar_azimuth : `float` |
| 125 | + Azimuth of the RHI scan in degrees. |
| 126 | +
|
| 127 | + Returns |
| 128 | + ---------- |
| 129 | + lma_file_loc : `numpy.ndarray` |
| 130 | + A Nx3 array representing the distance along, distance from, and height above the ground (in m) of the LMA VHF sources. |
| 131 | +
|
| 132 | + """ |
| 133 | + Xlma,Ylma,Zlma = geo_to_tps(lma_file, radar_latitude, radar_longitude, radar_altitude) |
| 134 | + X, Y, Z = rcs_to_tps(radar_latitude, radar_longitude, radar_altitude, radar_azimuth) |
| 135 | + |
| 136 | + lon_ini1 = X[0,0] |
| 137 | + lat_ini1 = Y[0,0] |
| 138 | + lon_fin1 = X[0,-1] |
| 139 | + lat_fin1 = Y[0,-1] |
| 140 | + |
| 141 | + |
| 142 | + dlon1 = lon_fin1 - lon_ini1 # dx |
| 143 | + dlat1 = lat_fin1 - lat_ini1 # dy |
| 144 | + ds1 = np.array((dlon1,dlat1)) |
| 145 | + norm_ds1 = np.linalg.norm(ds1) |
| 146 | + cross_ds1 = np.tensordot(ds1, ds1, (0,0)) |
| 147 | + |
| 148 | + # LMA |
| 149 | + lma_file_n = np.column_stack((Xlma, Ylma)) |
| 150 | + |
| 151 | + lma_file_loc_par = np.zeros(shape=(len(lma_file_n), 2)) |
| 152 | + lma_file_loc_perp = np.zeros(shape=(len(lma_file_n), 2)) |
| 153 | + lma_file_loc = np.zeros(shape=(len(lma_file_n), 3)) |
| 154 | + |
| 155 | + # |
| 156 | + # ################################## |
| 157 | + # |
| 158 | + # (Xlma[i],Ylma[i]).ds1 .ds1 |
| 159 | + # ---------------------- |
| 160 | + # ds1 . ds1 |
| 161 | + # |
| 162 | + # ################################## |
| 163 | + # |
| 164 | + lma_file_loc_tensor_x = np.tensordot(ds1,lma_file_n,(0,1)) |
| 165 | + lma_file_loc_par = np.tensordot((lma_file_loc_tensor_x / cross_ds1 ),ds1,0) |
| 166 | + |
| 167 | + # |
| 168 | + # ####################################################################### |
| 169 | + # |
| 170 | + # (Xlma[i],Ylma[i]) _ (Xlma[i],Ylma[i]).ds1 .ds1 |
| 171 | + # ---------------------- |
| 172 | + # ds1 . ds1 |
| 173 | + # |
| 174 | + ########################################################################## |
| 175 | + # |
| 176 | + lma_file_loc_perp = lma_file_n - lma_file_loc_par |
| 177 | + |
| 178 | + # |
| 179 | + lma_file_loc[:,0] = np.sqrt(lma_file_loc_par[:,0]**2 + lma_file_loc_par[:,1]**2) |
| 180 | + if radar_azimuth <= 90 or radar_azimuth >= 270: |
| 181 | + lma_file_loc[:,0][lma_file_loc_par[:,1] < 0] = -lma_file_loc[:,0][lma_file_loc_par[:,1] < 0] |
| 182 | + elif radar_azimuth > 180 and radar_azimuth < 270: |
| 183 | + lma_file_loc[:,0][lma_file_loc_par[:,0] > 0] = -lma_file_loc[:,0][lma_file_loc_par[:,0] > 0] |
| 184 | + else: |
| 185 | + lma_file_loc[:,0][lma_file_loc_par[:,0] < 0] = -lma_file_loc[:,0][lma_file_loc_par[:,0] < 0] |
| 186 | + lma_file_loc[:,1] = np.sqrt(lma_file_loc_perp[:,0]**2 + lma_file_loc_perp[:,1]**2) |
| 187 | + lma_file_loc[:,2] = Zlma |
| 188 | + |
| 189 | + return lma_file_loc |
| 190 | + |
| 191 | + |
| 192 | +def find_points_near_rhi(lma_file, radar_latitude, radar_longitude, radar_altitude, radar_azimuth, radar_scan_time, distance_threshold=1000, time_threshold=30): |
| 193 | + """ |
| 194 | + Find the LMA VHF sources near a radar RHI scan. |
| 195 | + |
| 196 | + Creates tangent plane at the radar's location and converts the LMA VHF sources to the tangent plane coordinate system, then rotates the tangent plane in the direction of the scan azimuth. |
| 197 | + Filters RHI scan points based on distance and time thresholds. |
| 198 | +
|
| 199 | + |
| 200 | + Parameters |
| 201 | + ---------- |
| 202 | + lma_file : `xarray.Dataset` |
| 203 | + A pyxlma dataset containing latitude, longitude, and altitude, and event_id of N number of LMA VHF sources. |
| 204 | + radar_latitude : `float` |
| 205 | + Latitude of the radar in degrees. |
| 206 | + radar_longitude : `float` |
| 207 | + Longitude of the radar in degrees. |
| 208 | + radar_altitude : `float` |
| 209 | + Altitude of the radar in meters. |
| 210 | + radar_azimuth : `float` |
| 211 | + Azimuth of the RHI scan in degrees. |
| 212 | + radar_scan_time : `datetime.datetime` or `numpy.datetime64` or `pandas.Timestamp` |
| 213 | + Time of the RHI scan. |
| 214 | + distance_threshold : `float` |
| 215 | + Maximum distance from the radar to the LMA VHF sources in meters. Default is 1000. |
| 216 | + time_threshold : `float` |
| 217 | + Number of seconds before and after the RHI scan time to include LMA VHF sources. Default is 30. (total length: 1 minute) |
| 218 | +
|
| 219 | +
|
| 220 | + Returns |
| 221 | + ---------- |
| 222 | + lma_range : `numpy.ndarray` |
| 223 | + A 1D array representing the distance along the tangent plane in the direction of the RHI scan. |
| 224 | + lma_dist : `numpy.ndarray` |
| 225 | + A 1D array representing the distance from the radar RHI scan plane to each filtered LMA point. |
| 226 | + lma_alt : `numpy.ndarray` |
| 227 | + A 1D array representing the height above the tangent plane centered at radar level of each filtered LMA point. |
| 228 | + point_mask : `numpy.ndarray` |
| 229 | + A 1D array of booleans representing the VHF points that were included in the return. |
| 230 | + """ |
| 231 | + |
| 232 | + radar_azimuth = radar_azimuth % 360 |
| 233 | + |
| 234 | + radar_scan_time = np.array([radar_scan_time]).astype('datetime64[s]') |
| 235 | + |
| 236 | + projected_lma = ortho_proj_lma(lma_file, radar_latitude, radar_longitude, radar_altitude, radar_azimuth) |
| 237 | + lma_range = projected_lma[:,0] |
| 238 | + lma_dist = projected_lma[:,1] |
| 239 | + lma_alt = projected_lma[:,2] |
| 240 | + |
| 241 | + lma_times = lma_file.event_time.data.astype('datetime64[s]') |
| 242 | + point_mask = (lma_dist < distance_threshold) & (np.abs(lma_times - radar_scan_time).astype(float) < time_threshold) |
| 243 | + lma_range = lma_range[point_mask] |
| 244 | + lma_dist = lma_dist[point_mask] |
| 245 | + lma_alt = lma_alt[point_mask] |
| 246 | + |
| 247 | + return lma_range, lma_dist, lma_alt, point_mask |
0 commit comments