001    /**
002     * 
003     */
004    package org.wdssii.singleradar;
005    
006    import org.apache.commons.logging.Log;
007    import org.apache.commons.logging.LogFactory;
008    import org.wdssii.core.Algorithm;
009    import org.wdssii.core.BuilderFactory;
010    import org.wdssii.core.DataEncoder;
011    import org.wdssii.core.DataType;
012    import org.wdssii.core.IndexRecord;
013    import org.wdssii.core.PolarGrid;
014    import org.wdssii.core.RadialSet;
015    import org.wdssii.core.VCP;
016    
017    /**
018     * @author lakshman
019     * 
020     */
021    public class VilAlgorithm extends Algorithm {
022            private static Log log = LogFactory.getLog(VilAlgorithm.class);
023    
024            private String inputType = "Reflectivity";
025    
026            private boolean vilEnabled = true;
027    
028            private boolean compositeEnabled = true;
029    
030            private boolean rapidUpdateEnabled = true;
031    
032            private float azimuthalSpacingDegrees = 1.0f;
033    
034            private float gateWidthKms = 1.0f;
035    
036            private int numGates = 500;
037    
038            private VCP vcp = null;
039    
040            private PolarGrid[] elevations = null;
041    
042            private int latest = -1;
043    
044            public String getInputType() {
045                    return inputType;
046            }
047    
048            public void setInputType(String inputType) {
049                    this.inputType = inputType;
050            }
051    
052            public boolean isCompositeEnabled() {
053                    return compositeEnabled;
054            }
055    
056            public void setCompositeEnabled(boolean compositeEnabled) {
057                    this.compositeEnabled = compositeEnabled;
058            }
059    
060            public boolean isRapidUpdateEnabled() {
061                    return rapidUpdateEnabled;
062            }
063    
064            public void setRapidUpdateEnabled(boolean rapidUpdateEnabled) {
065                    this.rapidUpdateEnabled = rapidUpdateEnabled;
066            }
067    
068            public boolean isVilEnabled() {
069                    return vilEnabled;
070            }
071    
072            public void setVilEnabled(boolean vilEnabled) {
073                    this.vilEnabled = vilEnabled;
074            }
075    
076            public float getAzimuthalSpacingDegrees() {
077                    return azimuthalSpacingDegrees;
078            }
079    
080            public void setAzimuthalSpacingDegrees(float azimuthalSpacing) {
081                    this.azimuthalSpacingDegrees = (azimuthalSpacing);
082            }
083    
084            public float getGateWidthKms() {
085                    return gateWidthKms;
086            }
087    
088            public void setGateWidthKms(float gateWidth) {
089                    this.gateWidthKms = (gateWidth);
090            }
091    
092            public int getNumGates() {
093                    return numGates;
094            }
095    
096            public void setNumGates(int numGates) {
097                    this.numGates = (numGates);
098            }
099    
100            /**
101             * Workhorse method of algorithm; will be called by Index on every new
102             * record.
103             */
104            public synchronized void handleRecord(IndexRecord rec) {
105                    try {
106                            if (log.isDebugEnabled()) {
107                                    log.debug("Received: " + rec);
108                            }
109                            if (!rec.getDataType().equals(inputType)) {
110                                    return;
111                            }
112                            RadialSet rs = (RadialSet) BuilderFactory.createDataType(rec);
113                            PolarGrid pg = new PolarGrid(rs, getAzimuthalSpacingDegrees(),
114                                            getGateWidthKms()*1000, getNumGates(), true);
115                            update(pg);
116                            if (isReadyToCompute()) {
117                                    String[] subtypes = getSubTypes();
118                                    PolarGrid composite = computeComposite();
119                                    if (compositeEnabled) {
120                                            DataEncoder.writeDataAndNotify(composite, getOutputDir(), subtypes);
121                                    }
122                                    if (vilEnabled) {
123                                            PolarGrid vil = computeVil(composite);
124                                            DataEncoder.writeDataAndNotify(vil, getOutputDir(), subtypes);
125                                    }
126                            }
127                    } catch (Exception e) {
128                            log.error("Logic error", e);
129                            // continue
130                    }
131            }
132    
133            private PolarGrid computeVil(PolarGrid composite) {
134                    PolarGrid example = composite;
135                    PolarGrid vil = new PolarGrid(example,"VIL", 0, 0);
136                    vil.addAttributes(example);
137                    vil.setAttribute("Unit", "kg/m^2");
138                    int numRadials = vil.getNumRadials();
139                    int numGates = vil.getNumGates();
140                    float[][] comp = composite.getValues();
141                    float[][] result = vil.getValues();
142                    float[] midbeam_sines = computeMidbeamSines();
143                    for (int gate = 0; gate < numGates; gate++) {
144                            for (int radial = 0; radial < numRadials; radial++) {
145                                    if (comp[radial][gate] <= 0) {
146                                            continue;
147                                    }
148                                    float temporary1 = 0;
149                                    for (int elev = 0; elev < elevations.length; elev++) {
150                                            // compute Z
151                                            float dbz = elevations[elev].getValues()[radial][gate];
152                                            if (dbz == DataType.MissingData) {
153                                                    continue;
154                                            }
155                                            float range = vil.getRangeToFirstGateKms() + (gate + 0.5f) * vil.getGateWidthKms();
156                                            // depth of beam
157                                            float hAgl;
158                                            if (elev == 0) {
159                                                    hAgl = heightAboveGround(range, midbeam_sines[elev]);
160                                            } else {
161                                                    float hAgl_this = heightAboveGround(range,
162                                                                    midbeam_sines[elev]);
163                                                    float hAgl_below = heightAboveGround(range,
164                                                                    midbeam_sines[elev - 1]);
165                                                    hAgl = hAgl_this - hAgl_below;
166                                            }
167                                            temporary1 += getPowPow(dbz) * hAgl;
168                                    }// elev
169                                    if (temporary1 <= 1)
170                                            result[radial][gate] = DataType.MissingData;
171                                    else
172                                            result[radial][gate] = temporary1;
173                            }// gate
174                    }// radial
175    
176                    return vil;
177            }
178    
179            private static final float minDBZ = -30;
180    
181            private static final float maxDBZ = 55;
182    
183            private static final float deltaDBZ = 0.5f;
184    
185            private static final float[] lookup = makePowPowLookup();
186    
187            private static float[] makePowPowLookup() {
188                    int n = Math.round((maxDBZ - minDBZ) / deltaDBZ) + 1;
189                    float[] lookup = new float[n];
190                    final double puissance = 4.0 / 7.0;
191                    for (int i = 0; i < n; ++i) {
192                            float dbz = minDBZ + i * deltaDBZ;
193                            double Z = Math.pow(10, (dbz / 10));
194                            lookup[i] = (float) (0.00000344 * Math.pow(Z, puissance));
195                    }
196                    return lookup;
197            }
198    
199            private float getPowPow(float dbz) {
200                    int bin = Math.round((dbz - minDBZ) / deltaDBZ);
201                    if (bin < 0)
202                            bin = 0;
203                    else if (bin >= lookup.length)
204                            bin = lookup.length - 1;
205                    return lookup[bin];
206            }
207    
208            private static final float FACTOR = (float) (2.0 * 1.21 * 6371.0);
209    
210            private float heightAboveGround(float range, float sine_angle) {
211                    float zhgt;
212                    float hgt;// , val;
213                    hgt = (range * sine_angle) + ((range * range) / FACTOR);
214                    zhgt = hgt * 1000;
215                    return (zhgt);
216            }
217    
218            private float[] computeMidbeamSines() {
219                    float[] midbeam_sines = new float[elevations.length];
220                    for (int elev = 0; elev < elevations.length; elev++) {
221                            float this_angle = elevations[elev].getElevation() + 0.5f
222                                            * elevations[elev].getAngularResolution(); // current
223                            midbeam_sines[elev] = (float) Math.sin(Math.toRadians(this_angle));
224                    }
225                    return midbeam_sines;
226            }
227    
228            private String[] getSubTypes() {
229                    float elev = elevations[latest].getElevation();
230                    String subType = getSubTypeForElevation(elev);
231                    String[] subtypes;
232                    if (isVolumeComplete()) {
233                            subtypes = new String[] { subType, "vol" };
234                    } else {
235                            subtypes = new String[] { subType };
236                    }
237                    return subtypes;
238            }
239    
240            private void update(PolarGrid pg) {
241                    // get vcp
242                    int newvcp = Integer.parseInt((String) pg.getAttribute("vcp"));
243                    if (vcp == null || newvcp != vcp.getVcp()) {
244                            if (log.isInfoEnabled()) {
245                                    log.info("VCP changed to " + newvcp
246                                                    + " resetting elevation history");
247                            }
248                            elevations = null;
249                            vcp = VCP.getVCP(newvcp);
250                            elevations = new PolarGrid[vcp.getLastTilt()];
251                    }
252    
253                    // find appropriate elevation
254                    int tiltNo = vcp.getTiltNumber(pg.getElevation());
255                    latest = tiltNo - 1;
256                    elevations[latest] = pg;
257                    if (log.isInfoEnabled()) {
258                            log.info("updated with " + latest + "th elevation at "
259                                            + pg.getElevation());
260                    }
261            }
262    
263            private boolean isVolumeComplete() {
264                    return (elevations != null && latest >= 0 && latest == (elevations.length - 1));
265            }
266    
267            private boolean isReadyToCompute() {
268                    if (elevations == null || elevations.length == 0) {
269                            log.debug("No elevations yet");
270                            return false;
271                    }
272                    for (int i = 0; i < elevations.length; ++i) {
273                            if (elevations[i] == null) {
274                                    if (log.isDebugEnabled()) {
275                                            log.debug(i + "th elevation not received");
276                                    }
277                                    return false;
278                            }
279                    }
280                    if (!rapidUpdateEnabled && !isVolumeComplete()) {
281                            log.debug("The latest elevation received is not last in volume");
282                            return false;
283                    }
284                    log.debug("Ready to compute");
285                    return true;
286            }
287    
288            private PolarGrid computeComposite() {
289                    PolarGrid example = elevations[latest];
290                    PolarGrid composite = new PolarGrid(example, example.getTypeName(), 0, 0);
291                    composite.addAttributes(example);
292                    int numRadials = composite.getNumRadials();
293                    int numGates = composite.getNumGates();
294                    float[][] result = composite.getValues();
295                    for (int e = 0; e < elevations.length; ++e) {
296                            float[][] values = elevations[e].getValues();
297                            for (int i = 0; i < numRadials; ++i) {
298                                    for (int j = 0; j < numGates; ++j) {
299                                            if (values[i][j] > result[i][j]) {
300                                                    result[i][j] = values[i][j];
301                                            }
302                                    }
303                            }
304                    }
305                    log.debug("composite computed");
306                    return composite;
307            }
308    
309            public static void main(String[] args) throws Exception {
310                    VilAlgorithm alg = new VilAlgorithm();
311                    alg.setupAndExecute(args);
312            }
313    
314    }