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