001 package org.wdssii.polarmerger; 002 003 import org.apache.commons.logging.Log; 004 import org.apache.commons.logging.LogFactory; 005 006 public class PowerDensityLookup { 007 008 private static Log log = LogFactory.getLog(PowerDensityLookup.class); 009 010 private float beamwidth; 011 private float azimuthalSpacingDegrees; 012 private float gateSpacingKms; 013 private int numGatesSmoothing; 014 private int numBeamsSpread; 015 private float[][][] weights; 016 017 public PowerDensityLookup(float beamwidth, float azimuthalSpacingDegrees, float gatewidthKms, float gateSpacingKms){ 018 log.info("Creating PowerDensityLookup"); 019 this.beamwidth = beamwidth; 020 this.azimuthalSpacingDegrees = azimuthalSpacingDegrees; 021 this.gateSpacingKms = gateSpacingKms; 022 this.numGatesSmoothing = 1 + Math.round(gatewidthKms / gateSpacingKms); 023 this.numBeamsSpread = 1 + Math.round(beamwidth / azimuthalSpacingDegrees); 024 this.weights = new float[2*numBeamsSpread+1][2*numBeamsSpread+1][2*numGatesSmoothing+1]; 025 026 // The lookup stores the weights at various points around a center point 027 float maxPower = 0; 028 float totPower = 0; 029 for (int e=-numBeamsSpread; e <= numBeamsSpread; ++e){ 030 float elev_power = computePowerDensity(((float)e)/numBeamsSpread); 031 for (int i=-numBeamsSpread; i <= numBeamsSpread; ++i){ 032 float az_power = computePowerDensity(((float)i)/numBeamsSpread); 033 for (int j=-numGatesSmoothing; j <= numGatesSmoothing; ++j){ 034 float rd_power = computePowerDensity(((float)j)/numGatesSmoothing); 035 float power = elev_power * az_power * rd_power; 036 weights[e+numBeamsSpread][i+numBeamsSpread][j+numGatesSmoothing] = power; 037 maxPower = Math.max(power, maxPower); 038 totPower += power; 039 } 040 } 041 } 042 for (int i=0; i < weights.length; ++i){ 043 for (int j=0; j < weights[0].length; ++j){ 044 for (int k=0; k < weights[0][0].length; ++k){ 045 weights[i][j][k] /= maxPower; 046 // System.out.println("wt(" + i + "," + j + "," + k + ")=" + weights[i][j][k]); 047 } 048 } 049 } 050 051 log.info("Finished creating PowerDensityLookup"); 052 } 053 054 public float getAzimuthalSpacingDegrees() { 055 return azimuthalSpacingDegrees; 056 } 057 058 public float getGateSpacingKms() { 059 return gateSpacingKms; 060 } 061 062 063 public float getBeamwidth() { 064 return beamwidth; 065 } 066 067 068 069 public int getNumBeamsSpread() { 070 return numBeamsSpread; 071 } 072 073 074 075 public int getNumGatesSmoothing() { 076 return numGatesSmoothing; 077 } 078 079 080 081 public float[][][] getWeights() { 082 return weights; 083 } 084 085 086 087 private static float computePowerDensity(float fracOfBeamWidthFromCenter) { 088 // eq. 3.2a of Doviak-Zrnic gives the power density function 089 // theta -- angular distance from the beam axis = frac * beamwidth 090 // sin(theta) is approx theta = dist*beamwidth 091 // lambda (eq.3.2b) = beamwidth*D / 1.27 092 // thus, Dsin(theta)/lambda = D * dist * beamwidth / (D*beamwidth/1.27) 093 // = 1.27 * dist 094 // and the weight is ( J_2(pi*1.27*dist)/(pi*1.27*dist)^2 )^2 095 // dist is between -1/2 and 1/2, so abs(pi*1.27*dist) < 2 096 // in this range, J_2(x) is approximately 1 - exp(-x^2/8.5) 097 double x = (Math.PI * 1.27 * fracOfBeamWidthFromCenter); 098 if (x < 0.0001 && x > -0.0001){ 099 x = 0.0001; // avoid divide-by-zero 100 } 101 double wt = (1 - Math.exp(-x * x / 8.5)) / (x * x); 102 wt = wt * wt; 103 return (float) (wt); 104 } 105 }