001 /*
002 * $Id: PolarOrbitTrackDataSource.java,v 1.14 2012/02/19 17:35:44 davep Exp $
003 *
004 * This file is part of McIDAS-V
005 *
006 * Copyright 2007-2012
007 * Space Science and Engineering Center (SSEC)
008 * University of Wisconsin - Madison
009 * 1225 W. Dayton Street, Madison, WI 53706, USA
010 * https://www.ssec.wisc.edu/mcidas
011 *
012 * All Rights Reserved
013 *
014 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and
015 * some McIDAS-V source code is based on IDV and VisAD source code.
016 *
017 * McIDAS-V is free software; you can redistribute it and/or modify
018 * it under the terms of the GNU Lesser Public License as published by
019 * the Free Software Foundation; either version 3 of the License, or
020 * (at your option) any later version.
021 *
022 * McIDAS-V is distributed in the hope that it will be useful,
023 * but WITHOUT ANY WARRANTY; without even the implied warranty of
024 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
025 * GNU Lesser Public License for more details.
026 *
027 * You should have received a copy of the GNU Lesser Public License
028 * along with this program. If not, see http://www.gnu.org/licenses.
029 */
030
031 package edu.wisc.ssec.mcidasv.data;
032
033 import java.io.BufferedReader;
034 import java.io.InputStreamReader;
035 import java.net.URL;
036 import java.net.URLConnection;
037 import java.rmi.RemoteException;
038 import java.util.ArrayList;
039 import java.util.Hashtable;
040 import java.util.List;
041 import java.util.Vector;
042
043 import org.slf4j.Logger;
044 import org.slf4j.LoggerFactory;
045
046 import ucar.unidata.data.DataCategory;
047 import ucar.unidata.data.DataChoice;
048 import ucar.unidata.data.DataSelection;
049 import ucar.unidata.data.DataSelectionComponent;
050 import ucar.unidata.data.DataSourceDescriptor;
051 import ucar.unidata.data.DataSourceImpl;
052 import ucar.unidata.data.DirectDataChoice;
053 import ucar.unidata.idv.IntegratedDataViewer;
054 import ucar.unidata.util.StringUtil;
055
056 import visad.CommonUnit;
057 import visad.Data;
058 import visad.Text;
059 import visad.Tuple;
060 import visad.Unit;
061 import visad.VisADException;
062 import visad.georef.LatLonTuple;
063
064 import edu.wisc.ssec.mcidas.adde.AddeTextReader;
065 import edu.wisc.ssec.mcidasv.chooser.PolarOrbitTrackChooser;
066 import edu.wisc.ssec.mcidasv.data.adde.sgp4.SGP4SatData;
067 import edu.wisc.ssec.mcidasv.data.adde.sgp4.SGP4unit;
068 import edu.wisc.ssec.mcidasv.data.adde.sgp4.SatelliteTleSGP4;
069 import edu.wisc.ssec.mcidasv.data.adde.sgp4.TLE;
070 import edu.wisc.ssec.mcidasv.data.adde.sgp4.Time;
071
072 /**
073 * Class for data sources of ADDE text data. These may be generic
074 * files or weather bulletins
075 *
076 * @author IDV development team
077 * @version $Revision: 1.14 $
078 */
079
080 public class PolarOrbitTrackDataSource extends DataSourceImpl {
081
082 private static final Logger logger = LoggerFactory.getLogger(PolarOrbitTrackDataSource.class);
083
084 /** list of twod categories */
085 private List twoDCategories;
086
087 private List tleCards = new ArrayList();
088 private List choices = new ArrayList();
089
090 private SGP4SatData data = new SGP4SatData();
091 private TLE tle;
092
093 public static double pi = SGP4unit.pi;
094
095 private Hashtable selectionProps;
096
097 /** time step between data points */
098 private int dTime = 1;
099
100 private SatelliteTleSGP4 prop = null;
101 private double julDate0 = 0.0;
102 private double julDate1 = 0.0;
103
104 /**
105 * Default bean constructor for persistence; does nothing.
106 */
107 public PolarOrbitTrackDataSource() {}
108
109 /**
110 * Create a new PolarOrbitTrackDataSource
111 *
112 * @param descriptor descriptor for this source
113 * @param filename ADDE URL
114 * @param properties extra properties for this source
115 *
116 */
117 public PolarOrbitTrackDataSource(DataSourceDescriptor descriptor,
118 String filename, Hashtable properties)
119 throws VisADException {
120 super(descriptor, filename, null, properties);
121 /*
122 System.out.println("\nPolarOrbitTrackDataSource:");
123 System.out.println(" descriptor=" + descriptor);
124 System.out.println(" filename=" + filename);
125 */
126 tleCards = new ArrayList();
127 choices = new ArrayList();
128 String key = PolarOrbitTrackChooser.TLE_SERVER_NAME_KEY;
129 if (properties.containsKey(key)) {
130 Object server = properties.get(key);
131 key = PolarOrbitTrackChooser.TLE_GROUP_NAME_KEY;
132 Object group = properties.get(key);
133 key = PolarOrbitTrackChooser.TLE_USER_ID_KEY;
134 Object user = properties.get(key);
135 key = PolarOrbitTrackChooser.TLE_PROJECT_NUMBER_KEY;
136 Object proj = properties.get(key);
137 key = PolarOrbitTrackChooser.DATASET_NAME_KEY;
138 Object descr = properties.get(key);
139 String url = "adde://" + server + "/textdata?&PORT=112&COMPRESS=gzip&USER=" + user + "&PROJ=" + proj + "&GROUP=" + group + "&DESCR=" + descr;
140 AddeTextReader reader = new AddeTextReader(url);
141 List lines = null;
142 if ("OK".equals(reader.getStatus())) {
143 lines = reader.getLinesOfText();
144 }
145 if (lines == null) {
146 notTLE();
147 return;
148 } else {
149 String[] cards = StringUtil.listToStringArray(lines);
150 for (int i=0; i<cards.length; i++) {
151 String str = cards[i];
152 if (str.length() > 0) {
153 tleCards.add(cards[i]);
154 int indx = cards[i].indexOf(" ");
155 if (indx < 0) {
156 choices.add(cards[i]);
157 }
158 }
159 }
160 }
161 } else {
162 try {
163 key = PolarOrbitTrackChooser.URL_NAME_KEY;
164 String urlStr = (String)(properties.get(key));
165 URL url = new URL(urlStr);
166 URLConnection urlCon = url.openConnection();
167 InputStreamReader isr = new InputStreamReader(urlCon.getInputStream());
168 BufferedReader tleReader = new BufferedReader(isr);
169 String nextLine = null;
170 while ((nextLine = tleReader.readLine()) != null) {
171 if (nextLine.length() > 0) {
172 tleCards.add(nextLine);
173 if (nextLine.length() < 50) {
174 choices.add(nextLine);
175 }
176 }
177 }
178 } catch (Exception e) {
179 notTLE();
180 return;
181 }
182 }
183 checkFirstEntry();
184 }
185
186 private void checkFirstEntry() {
187 if (tleCards.isEmpty()) {
188 notTLE();
189 return;
190 }
191 String card = (String)tleCards.get(1);
192 decodeCard1(card);
193 }
194
195 public void initAfterCreation() {
196 }
197
198 /**
199 * Make the data choices assoicated with this source.
200 */
201 protected void doMakeDataChoices() {
202 String category = "TLE";
203 for (int i=0; i<choices.size(); i++) {
204 String name = ((String)choices.get(i)).trim();
205 addDataChoice(
206 new DirectDataChoice(
207 this, name, name, name,
208 DataCategory.parseCategories(category, false)));
209 }
210 }
211
212 /**
213 * Initialize the {@link ucar.unidata.data.DataCategory} objects that
214 * this data source uses.
215 */
216 private void makeCategories() {
217 twoDCategories = DataCategory.parseCategories("TLE", false);
218 }
219
220 /**
221 * Actually get the data identified by the given DataChoce. The default is
222 * to call the getDataInner that does not take the requestProperties. This
223 * allows other, non unidata.data DataSource-s (that follow the old API)
224 * to work.
225 *
226 * @param dataChoice The data choice that identifies the requested
227 * data.
228 * @param category The data category of the request.
229 * @param dataSelection Identifies any subsetting of the data.
230 * @param requestProperties Hashtable that holds any detailed request
231 * properties.
232 *
233 * @return The visad.Text object
234 *
235 * @throws RemoteException Java RMI problem
236 * @throws VisADException VisAD problem
237 */
238
239 protected Data getDataInner(DataChoice dataChoice, DataCategory category,
240 DataSelection dataSelection,
241 Hashtable requestProperties)
242 throws VisADException, RemoteException {
243 /*
244 System.out.println("\ngetDataInner:");
245 System.out.println(" dTime=" + dTime);
246 System.out.println(" dataChoice=" + dataChoice);
247 System.out.println(" category=" + category);
248 System.out.println(" dataSelection=" + dataSelection + "\n");
249 System.out.println("categories for dataChoice: " + dataChoice.getCategories());
250 */
251
252 boolean gotit = false;
253 int index = -1;
254 String choiceName = dataChoice.getName();
255 String tleLine1 = "";
256 String tleLine2 = "";
257
258 while(!gotit) {
259 index++;
260 String name = ((String)tleCards.get(index)).trim();
261 if (name.equals(choiceName)) {
262 data.name = name;
263 /*
264 System.out.println("\n" + tleCards.get(index));
265 System.out.println(tleCards.get(index+1));
266 System.out.println(tleCards.get(index+2) + "\n");
267 */
268 index++;
269 String card = (String)tleCards.get(index);
270 tleLine1 = card;
271 int ncomps = decodeCard1(card);
272 if (ncomps < 0) return null;
273 index++;
274 card = (String)tleCards.get(index);
275 tleLine2 = card;
276 ncomps += decodeCard2(card);
277 gotit= true;
278 }
279 if (index+3 > tleCards.size()) gotit = true;
280 }
281 if (gotit == false) return null;
282
283 this.selectionProps = dataSelection.getProperties();
284 /*
285 Enumeration propEnum = this.selectionProps.keys();
286 for (int i = 0; propEnum.hasMoreElements(); i++) {
287 String key = propEnum.nextElement().toString();
288 String val = (String)this.selectionProps.get(key);
289 System.out.println("key=" + key + " val=" + val);
290 }
291 */
292 tle = new TLE(choiceName, tleLine1, tleLine2);
293
294 String endStr = (String)this.selectionProps.get("ETime");
295 Double dEnd = new Double(endStr);
296 double endJulianDate = dEnd.doubleValue();
297 julDate1 = endJulianDate;
298
299 try
300 {
301 prop = new SatelliteTleSGP4(tle.getSatName(), tle.getLine1(), tle.getLine2());
302 prop.setShowGroundTrack(false);
303 }
304 catch(Exception e)
305 {
306 logger.error("Error Creating SGP4 Satellite e=" + e);
307 System.exit(1);
308 }
309
310 Time time = new Time(
311 (new Integer((String)this.selectionProps.get("Year"))).intValue(),
312 (new Integer((String)this.selectionProps.get("Month"))).intValue(),
313 (new Integer((String)this.selectionProps.get("Day"))).intValue(),
314 (new Integer((String)this.selectionProps.get("Hours"))).intValue(),
315 (new Integer((String)this.selectionProps.get("Mins"))).intValue(),
316 (new Double((String)this.selectionProps.get("Secs"))).doubleValue());
317 double julianDate = time.getJulianDate();
318 julDate0 = julianDate;
319 Vector v = new Vector();
320
321 while (julianDate <= julDate1) {
322 // prop to the desired time
323 prop.propogate2JulDate(julianDate);
324
325 // get the lat/long/altitude [radians, radians, meters]
326 double[] lla = prop.getLLA();
327 double lat = lla[0]*180.0/Math.PI;
328 double lon = lla[1]*180.0/Math.PI;
329
330 /*
331 System.out.println(time.getDateTimeStr() + " Lat: " + lat
332 + " Lon: " + lon
333 + " Alt: " + alt);
334 */
335 Tuple data = new Tuple(new Data[] { new Text(time.getDateTimeStr()),
336 new LatLonTuple(
337 lat,
338 lon
339 )}
340 );
341 v.add(data);
342 time.add(Time.MINUTE, dTime);
343 julianDate = time.getJulianDate();
344 }
345
346 return new Tuple((Data[]) v.toArray(new Data[v.size()]), false);
347 }
348
349 public double getNearestAltToGroundStation(double gsLat, double gsLon) {
350 double retAlt = 0.0;
351 Time time = new Time(
352 (new Integer((String)this.selectionProps.get("Year"))).intValue(),
353 (new Integer((String)this.selectionProps.get("Month"))).intValue(),
354 (new Integer((String)this.selectionProps.get("Day"))).intValue(),
355 (new Integer((String)this.selectionProps.get("Hours"))).intValue(),
356 (new Integer((String)this.selectionProps.get("Mins"))).intValue(),
357 (new Double((String)this.selectionProps.get("Secs"))).doubleValue());
358
359 double minDist = 999999.99;
360 double julianDate = julDate0;
361
362 while (julianDate <= julDate1) {
363 // prop to the desired time
364 prop.propogate2JulDate(julianDate);
365
366 // get the lat/long/altitude [radians, radians, meters]
367 double[] lla = prop.getLLA();
368 double lat = lla[0]*180.0/Math.PI;
369 double lon = lla[1]*180.0/Math.PI;
370 double alt = lla[2];
371 //System.out.println(" " + time.getDateTimeStr() + ": lat=" + lat + " lon=" + lon + " alt=" + alt);
372
373 double latDiff = (gsLat - lat) * (gsLat - lat);
374 double lonDiff = (gsLon - lon) * (gsLon - lon);
375 double dist = Math.sqrt(latDiff+lonDiff);
376 if (dist < minDist) {
377 minDist = dist;
378 retAlt = alt;
379 }
380 time.add(Time.MINUTE, dTime);
381 julianDate = time.getJulianDate();
382 }
383
384 return retAlt;
385 }
386
387 private int decodeCard1(String card) {
388 /*
389 System.out.println("\ndecodeCard1:");
390 System.out.println(" card=" + card);
391 System.out.println(" length=" + card.length());
392 */
393 int satId = 0;
394 double ddd = 1.0;
395 double firstDev = 1.0;
396 int ephemerisType = 0;
397 int elementNumber = 0;
398
399 int ret = 0;
400 if (card.length() < 69) {
401 notTLE();
402 return -1;
403 }
404 int ck1 = checksum(card.substring(0, 68));
405 String str = card.substring(0, 1);
406 if (str.equals("1")) {
407 satId = getInt(2, 7, card);
408 //System.out.println(" satId = " + satId);
409 data.satnum = satId;
410 ++ret;
411
412 data.classification = card.substring(7, 8);
413 data.intldesg = card.substring(9, 17);
414 int yy = getInt(18, 20, card);
415 data.epochyr = yy;
416 ++ret;
417
418 ddd = getDouble(20, 32, card);
419 //System.out.println(" ddd = " + ddd);
420 data.epochdays = ddd;
421 ++ret;
422
423 firstDev = getDouble(33, 43, card);
424 //System.out.println(" firstDev = " + firstDev);
425 data.ndot = firstDev;
426 ++ret;
427
428 if((card.substring(44, 52)).equals(" "))
429 {
430 data.nddot = 0;
431 data.nexp = 0;
432 }
433 else
434 {
435 data.nddot = getDouble(44, 50, card) / 1.0E5;
436 data.nexp = getInt(50, 52, card);
437 }
438 //System.out.println(" nddot=" + data.nddot);
439 //System.out.println(" nexp=" + data.nexp);
440
441 data.bstar = getDouble(53, 59, card) / 1.0E5;
442 data.ibexp = getInt(59, 61, card);
443 //System.out.println(" bstar=" + data.bstar);
444 //System.out.println(" ibexp=" + data.ibexp);
445
446 try {
447 ephemerisType = getInt(62, 63, card);
448 //System.out.println(" ephemerisType = " + ephemerisType);
449 data.numb = ephemerisType;
450 ++ret;
451
452 elementNumber = getInt(64, 68, card);
453 //System.out.println(" elementNumber = " + elementNumber);
454 data.elnum = elementNumber;
455 ++ret;
456 } catch (Exception e) {
457 logger.error("Warning: Error Reading numb or elnum from TLE line 1 sat#:" + data.satnum);
458 }
459
460 int check = card.codePointAt(68) - 48;
461 if (check != ck1) {
462 notTLE();
463 // logger.error("***** Failed checksum *****");
464 ret = -1;
465 }
466 }
467 return ret;
468 }
469
470 private int decodeCard2(String card) {
471 /*
472 System.out.println("\ndecodeCard2:");
473 System.out.println(" card=" + card);
474 System.out.println(" length=" + card.length());
475 */
476 double inclination = 1.0;
477 double rightAscension = 1.0;
478 double eccentricity = 1.0;
479 double argOfPerigee = 1.0;
480 double meanAnomaly = 1.0;
481 double meanMotion = 1.0;
482 int revolutionNumber = 0;
483
484 int ret = 0;
485 //System.out.println("\n" + card);
486 if (card.length() < 69) {
487 notTLE();
488 return -1;
489 }
490 int ck1 = checksum(card.substring(0, 68));
491 String str = card.substring(0, 1);
492 if (str.equals("2")) {
493 int nsat = getInt(2, 7, card);
494 //System.out.println(" nsat = " + nsat + " data.satnum=" + data.satnum);
495 if (nsat != data.satnum) {
496 logger.error("Warning TLE line 2 Sat Num doesn't match line1 for sat: " + data.name);
497 } else {
498 inclination = getDouble(8, 16, card);
499 data.inclo = inclination;
500 //System.out.println(" inclo = " + data.inclo);
501 ++ret;
502
503 rightAscension = getDouble(17, 25, card);
504 data.nodeo = rightAscension;
505 //System.out.println(" nodeo = " + data.nodeo);
506 ++ret;
507
508 eccentricity = getDouble(26, 33, card) / 1.0E7;
509 data.ecco = eccentricity;
510 //System.out.println(" ecco = " + data.ecco);
511 ++ret;
512
513 argOfPerigee = getDouble(34, 42, card);
514 data.argpo = argOfPerigee;
515 //System.out.println(" argpo = " + data.argpo);
516 ++ret;
517
518 meanAnomaly = getDouble(43, 51, card);
519 data.mo = meanAnomaly;
520 //System.out.println(" mo = " + data.mo);
521 ++ret;
522
523 meanMotion = getDouble(52, 63, card);
524 data.no = meanMotion;
525 //System.out.println(" no = " + data.no);
526 ++ret;
527
528 try {
529 revolutionNumber = getInt(63, 68, card);
530 data.revnum = revolutionNumber;
531 //System.out.println(" revnum = " + data.revnum);
532 ++ret;
533 } catch (Exception e) {
534 logger.error("Warning: Error Reading revnum from TLE line 2 sat#:" + data.satnum + "\n" + e.toString());
535 data.revnum = -1;
536 }
537
538 int check = card.codePointAt(68) - 48;
539 if (check != ck1) {
540 notTLE();
541 // logger.error("***** Failed checksum *****");
542 ret = -1;
543 }
544 }
545 }
546 return ret;
547 }
548
549 private int getInt(int beg, int end, String card) {
550 String str = card.substring(beg, end);
551 str = str.trim();
552 return (new Integer(str)).intValue();
553 }
554
555 private double getDouble(int beg, int end, String card) {
556 String str = card.substring(beg, end);
557 str = str.trim();
558 return (new Double(str)).doubleValue();
559 }
560
561 private int checksum(String str) {
562 int sum = 0;
563 byte[] bites = str.getBytes();
564 for (int i=0; i<bites.length; i++) {
565 int val = (int)bites[i];
566 if ((val > 47) && (val < 58)) {
567 sum += val - 48;
568 } else if (val == 45) {
569 ++sum;
570 }
571 }
572 return sum % 10;
573 }
574
575 protected void initDataSelectionComponents(
576 List<DataSelectionComponent> components, final DataChoice dataChoice) {
577 /*
578 System.out.println("\ninitDataSelectionComponents:");
579 System.out.println(" components=" + components);
580 System.out.println(" dataChoice=" + dataChoice);
581 System.out.println(" categories=" + dataChoice.getCategories());
582 System.out.println(" displayCategory=" + dataChoice.getDisplayCategory());
583 */
584 clearTimes();
585 IntegratedDataViewer idv = getDataContext().getIdv();
586 idv.showWaitCursor();
587 try {
588 TimeRangeSelection timeSelection = new TimeRangeSelection(this);
589 components.add(timeSelection);
590 } catch (Exception e) {
591 logger.error("problem creating TimeRangeSelection e=" + e);
592 }
593 idv.showNormalCursor();
594 }
595
596 /**
597 * Show the dialog
598 *
599 * @param initTabName What tab should we show. May be null.
600 * @param modal Is dialog modal
601 *
602 * @return success
603 */
604 public boolean showPropertiesDialog(String initTabName, boolean modal) {
605 //System.out.println("\n\nshowPropertiesDialog:");
606 boolean ret = super.showPropertiesDialog(initTabName, modal);
607 return ret;
608 }
609
610 public int getDTime() {
611 return dTime;
612 }
613
614 public void setDTime(int val) {
615 //System.out.println("PolarOrbitTrackDataSource setDTime: val=" + val);
616 dTime = val;
617 }
618
619 private void notTLE() {
620 tleCards = new ArrayList();
621 choices = new ArrayList();
622 setInError(true, "\nSource does not contain TLE data");
623 }
624 }
625