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