001/* 002 * $Id: PolarOrbitTrackDataSource.java,v 1.8 2011/03/24 16:06:32 davep Exp $ 003 * 004 * This file is part of McIDAS-V 005 * 006 * Copyright 2007-2011 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 031package edu.wisc.ssec.mcidasv.data; 032 033import edu.wisc.ssec.mcidas.adde.AddeTextReader; 034import edu.wisc.ssec.mcidasv.chooser.PolarOrbitTrackChooser; 035import edu.wisc.ssec.mcidasv.data.adde.sgp4.*; 036 037import ucar.unidata.data.DataCategory; 038import ucar.unidata.data.DataChoice; 039import ucar.unidata.data.DataSelection; 040import ucar.unidata.data.DataSelectionComponent; 041import ucar.unidata.data.DataSourceDescriptor; 042import ucar.unidata.data.DataSourceImpl; 043import ucar.unidata.data.DirectDataChoice; 044import ucar.unidata.data.sounding.TrackDataSource; 045import ucar.unidata.idv.IntegratedDataViewer; 046import ucar.unidata.idv.ViewManager; 047import ucar.unidata.idv.ui.DataSelectionWidget; 048import ucar.unidata.util.GuiUtils; 049import ucar.unidata.util.LogUtil; 050import ucar.unidata.util.Misc; 051import ucar.unidata.util.StringUtil; 052import ucar.unidata.view.geoloc.MapProjectionDisplay; 053 054import visad.CommonUnit; 055import visad.CoordinateSystem; 056import visad.Data; 057import visad.DataReference; 058import visad.DateTime; 059import visad.Text; 060import visad.Tuple; 061import visad.Unit; 062import visad.VisADException; 063import visad.VisADException; 064import visad.georef.LatLonTuple; 065 066import visad.data.mcidas.AddeTextAdapter; 067 068import java.awt.Insets; 069import java.awt.Dimension; 070 071import java.io.BufferedReader; 072import java.io.FileInputStream; 073import java.io.InputStreamReader; 074 075import java.net.URL; 076import java.net.URLConnection; 077 078import java.rmi.RemoteException; 079 080import java.util.ArrayList; 081import java.util.Enumeration; 082import java.util.Hashtable; 083import java.util.List; 084import java.util.Vector; 085 086import javax.swing.BorderFactory; 087import javax.swing.JComponent; 088import javax.swing.JEditorPane; 089import javax.swing.JLabel; 090import javax.swing.JPanel; 091import javax.swing.JScrollPane; 092import javax.swing.JTabbedPane; 093 094import org.slf4j.Logger; 095import org.slf4j.LoggerFactory; 096 097/** 098 * Class for data sources of ADDE text data. These may be generic 099 * files or weather bulletins 100 * 101 * @author IDV development team 102 * @version $Revision: 1.8 $ 103 */ 104 105public class PolarOrbitTrackDataSource extends TrackDataSource { 106//public class PolarOrbitTrackDataSource extends DataSourceImpl { 107 108 private static final Logger logger = LoggerFactory.getLogger(PolarOrbitTrackDataSource.class); 109 110 private List tleCards = new ArrayList(); 111 private List choices = new ArrayList(); 112 113 private SGP4SatData data = new SGP4SatData(); 114 private TLE tle; 115 116 public static double pi = SGP4unit.pi; 117 118 private Hashtable selectionProps; 119 120 private double[] lla = new double[3]; 121 122 /** time step between data points */ 123 private int dTime = 5; 124 125 private SatelliteTleSGP4 prop = null; 126 private double julDate0 = 0.0; 127 private double julDate1 = 0.0; 128 129 /** 130 * Default bean constructor for persistence; does nothing. 131 */ 132 public PolarOrbitTrackDataSource() {} 133 134 /** 135 * Create a new PolarOrbitTrackDataSource 136 * 137 * @param descriptor descriptor for this source 138 * @param filename ADDE URL 139 * @param properties extra properties for this source 140 * 141 */ 142 public PolarOrbitTrackDataSource(DataSourceDescriptor descriptor, 143 String filename, Hashtable properties) 144 throws VisADException { 145 super(descriptor, filename, properties); 146/* 147 System.out.println("\nPolarOrbitTrackDataSource:"); 148 System.out.println(" descriptor=" + descriptor); 149 System.out.println(" filename=" + filename); 150*/ 151 tleCards = new ArrayList(); 152 choices = new ArrayList(); 153 String key = PolarOrbitTrackChooser.TLE_SERVER_NAME_KEY; 154 if (properties.containsKey(key)) { 155 Object server = properties.get(key); 156 key = PolarOrbitTrackChooser.TLE_GROUP_NAME_KEY; 157 Object group = properties.get(key); 158 key = PolarOrbitTrackChooser.TLE_USER_ID_KEY; 159 Object user = properties.get(key); 160 key = PolarOrbitTrackChooser.TLE_PROJECT_NUMBER_KEY; 161 Object proj = properties.get(key); 162 String url = "adde://" + server + "/textdata?&PORT=112&COMPRESS=gzip&USER=" + user + "&PROJ=" + proj + "&GROUP=" + group + "&DESCR=" + filename; 163 //System.out.println("\n" + url + "\n"); 164 AddeTextReader reader = new AddeTextReader(url); 165 List lines = null; 166 if ("OK".equals(reader.getStatus())) { 167 lines = reader.getLinesOfText(); 168 } 169 if (lines == null) { 170 //System.out.println("\nproblem reading TLE file"); 171 JLabel label = new JLabel("Invalid TLE data"); 172 JPanel contents = GuiUtils.top(GuiUtils.inset(label, label.getText().length() + 60)); 173 GuiUtils.showOkDialog(null, " Can't Make Orbit Tracks ", contents, null); 174 getDataContext().getIdv().showNormalCursor(); 175 logger.error("problem reading TLE file"); 176 return; 177 } else { 178 String[] cards = StringUtil.listToStringArray(lines); 179 //System.out.println("\n"); 180 for (int i=0; i<cards.length; i++) { 181 //System.out.println(cards[i]); 182 tleCards.add(cards[i]); 183 int indx = cards[i].indexOf(" "); 184 if (indx < 0) { 185 choices.add(cards[i]); 186 } 187 } 188 //System.out.println("\n"); 189 } 190 } else { 191 try { 192 key = PolarOrbitTrackChooser.URL_NAME_KEY; 193 String urlStr = (String)(properties.get(key)); 194 URL url = new URL(urlStr); 195 URLConnection urlCon = url.openConnection(); 196 InputStreamReader isr = new InputStreamReader(urlCon.getInputStream()); 197 BufferedReader tleReader = new BufferedReader(isr); 198 String nextLine = null; 199 int tleCount = 0; 200 while ((nextLine = tleReader.readLine()) != null) { 201 tleCards.add(nextLine); 202 if (nextLine.length() < 50) { 203 choices.add(nextLine); 204 } 205 } 206 } catch (Exception e) { 207 logger.error("can't read TLE url e=" + e); 208 return; 209 } 210 } 211 } 212 213 public void initAfterCreation() { 214 } 215 216 /** 217 * Make the data choices assoicated with this source. 218 */ 219 protected void doMakeDataChoices() { 220 String category = "TLE"; 221 for (int i=0; i<choices.size(); i++) { 222 String name = ((String)choices.get(i)).trim(); 223 addDataChoice( 224 new DirectDataChoice( 225 this, name, name, name, 226 DataCategory.parseCategories(category, false))); 227 } 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/* code example in 250 /home/gad/src/JSatTrak/JSatTrak-4.1-src/JSatTrak/src/name/gano/astro/propogators/sgp4_cssi/SGP4utils.java 251*/ 252 253 protected Data getDataInner(DataChoice dataChoice, DataCategory category, 254 DataSelection dataSelection, 255 Hashtable requestProperties) 256 throws VisADException, RemoteException { 257/* 258 System.out.println("\ngetDataInner:"); 259 System.out.println(" dTime=" + dTime); 260 System.out.println(" dataChoice=" + dataChoice); 261 System.out.println(" category=" + category); 262 System.out.println(" dataSelection=" + dataSelection + "\n"); 263 System.out.println("categories for dataChoice: " + dataChoice.getCategories()); 264*/ 265 final double deg2rad = pi / 180.0; // 0.0174532925199433 266 final double xpdotp = 1440.0 / (2.0 * pi); // 229.1831180523293 267 268 double sec, tumin; 269 int year = 0; 270 int mon, day, hr, minute;//, nexp, ibexp; 271 272 boolean gotit = false; 273 int index = -1; 274 String choiceName = dataChoice.getName(); 275 String tleLine1 = ""; 276 String tleLine2 = ""; 277 List tleComps = new ArrayList(); 278 while(!gotit) { 279 index++; 280 String name = ((String)tleCards.get(index)).trim(); 281 if (name.equals(choiceName)) { 282 data.name = name; 283/* 284 System.out.println("\n" + tleCards.get(index)); 285 System.out.println(tleCards.get(index+1)); 286 System.out.println(tleCards.get(index+2) + "\n"); 287*/ 288 index++; 289 String card = (String)tleCards.get(index); 290 tleLine1 = card; 291 int ncomps = decodeCard1(card); 292 if (ncomps < 0) return null; 293 index++; 294 card = (String)tleCards.get(index); 295 tleLine2 = card; 296 ncomps += decodeCard2(card); 297 gotit= true; 298 } 299 if (index+3 > tleCards.size()) gotit = true; 300 } 301 if (gotit == false) return null; 302 303 this.selectionProps = dataSelection.getProperties(); 304/* 305 Enumeration propEnum = this.selectionProps.keys(); 306 for (int i = 0; propEnum.hasMoreElements(); i++) { 307 String key = propEnum.nextElement().toString(); 308 String val = (String)this.selectionProps.get(key); 309 System.out.println("key=" + key + " val=" + val); 310 } 311*/ 312 tle = new TLE(choiceName, tleLine1, tleLine2); 313 314 String begStr = (String)this.selectionProps.get("BTime"); 315 Double dBeg = new Double(begStr); 316 double begJulianDate = dBeg.doubleValue(); 317 318 String endStr = (String)this.selectionProps.get("ETime"); 319 Double dEnd = new Double(endStr); 320 double endJulianDate = dEnd.doubleValue(); 321 julDate1 = endJulianDate; 322 323 try 324 { 325 prop = new SatelliteTleSGP4(tle.getSatName(), tle.getLine1(), tle.getLine2()); 326 prop.setShowGroundTrack(false); 327 } 328 catch(Exception e) 329 { 330 logger.error("Error Creating SGP4 Satellite e=" + e); 331 System.exit(1); 332 } 333 334 Time time = new Time( 335 (new Integer((String)this.selectionProps.get("Year"))).intValue(), 336 (new Integer((String)this.selectionProps.get("Month"))).intValue(), 337 (new Integer((String)this.selectionProps.get("Day"))).intValue(), 338 (new Integer((String)this.selectionProps.get("Hours"))).intValue(), 339 (new Integer((String)this.selectionProps.get("Mins"))).intValue(), 340 (new Double((String)this.selectionProps.get("Secs"))).doubleValue()); 341 double julianDate = time.getJulianDate(); 342 julDate0 = julianDate; 343 Unit unit = CommonUnit.secondsSinceTheEpoch; 344 Vector v = new Vector(); 345 346 while (julianDate <= julDate1) { 347 // prop to the desired time 348 prop.propogate2JulDate(julianDate); 349 350 // get the lat/long/altitude [radians, radians, meters] 351 double[] lla = prop.getLLA(); 352 double lat = lla[0]*180.0/Math.PI; 353 double lon = lla[1]*180.0/Math.PI; 354 double alt = lla[2]; 355/* 356 System.out.println(time.getDateTimeStr() + " Lat: " + lat 357 + " Lon: " + lon 358 + " Alt: " + alt); 359 */ 360 Tuple data = new Tuple(new Data[] { new Text(time.getDateTimeStr()), 361 new LatLonTuple( 362 lat, 363 lon 364 )} 365 ); 366 v.add(data); 367 time.add(Time.MINUTE, dTime); 368 julianDate = time.getJulianDate(); 369 } 370 371 return new Tuple((Data[]) v.toArray(new Data[v.size()]), false); 372 } 373 374 public double getNearestAltToGroundStation(double gsLat, double gsLon) { 375 double retAlt = 0.0; 376 Time time = new Time( 377 (new Integer((String)this.selectionProps.get("Year"))).intValue(), 378 (new Integer((String)this.selectionProps.get("Month"))).intValue(), 379 (new Integer((String)this.selectionProps.get("Day"))).intValue(), 380 (new Integer((String)this.selectionProps.get("Hours"))).intValue(), 381 (new Integer((String)this.selectionProps.get("Mins"))).intValue(), 382 (new Double((String)this.selectionProps.get("Secs"))).doubleValue()); 383 384 double minDist = 999999.99; 385 double julianDate = julDate0; 386 387 while (julianDate <= julDate1) { 388 // prop to the desired time 389 prop.propogate2JulDate(julianDate); 390 391 // get the lat/long/altitude [radians, radians, meters] 392 double[] lla = prop.getLLA(); 393 double lat = lla[0]*180.0/Math.PI; 394 double lon = lla[1]*180.0/Math.PI; 395 double alt = lla[2]; 396 //System.out.println(" " + time.getDateTimeStr() + ": lat=" + lat + " lon=" + lon + " alt=" + alt); 397 398 double latDiff = (gsLat - lat) * (gsLat - lat); 399 double lonDiff = (gsLon - lon) * (gsLon - lon); 400 double dist = Math.sqrt(latDiff+lonDiff); 401 if (dist < minDist) { 402 minDist = dist; 403 retAlt = alt; 404 } 405 time.add(Time.MINUTE, dTime); 406 julianDate = time.getJulianDate(); 407 } 408 409 return retAlt; 410 } 411 412 private int decodeCard1(String card) { 413/* 414 System.out.println("\ndecodeCard1:"); 415 System.out.println(" card=" + card); 416 System.out.println(" length=" + card.length()); 417*/ 418 int satId = 0; 419 int launchYear = 0; 420 int intCode = 0; 421 int yyyy = 0; 422 double ddd = 1.0; 423 double firstDev = 1.0; 424 double secondDev = 1.0; 425 double bStar = 1.0; 426 int ephemerisType = 0; 427 int elementNumber = 0; 428 429 int ret = 0; 430 //System.out.println(card); 431 int ck1 = checksum(card.substring(0, 68)); 432 String str = card.substring(0, 1); 433 if (str.equals("1")) { 434 satId = getInt(2, 7, card); 435 //System.out.println(" satId = " + satId); 436 data.satnum = satId; 437 ++ret; 438 439 data.classification = card.substring(7, 8); 440 data.intldesg = card.substring(9, 17); 441 int yy = getInt(18, 20, card); 442 data.epochyr = yy; 443 ++ret; 444 445 ddd = getDouble(20, 32, card); 446 //System.out.println(" ddd = " + ddd); 447 data.epochdays = ddd; 448 ++ret; 449 450 firstDev = getDouble(33, 43, card); 451 //System.out.println(" firstDev = " + firstDev); 452 data.ndot = firstDev; 453 ++ret; 454 455 if((card.substring(44, 52)).equals(" ")) 456 { 457 data.nddot = 0; 458 data.nexp = 0; 459 } 460 else 461 { 462 data.nddot = getDouble(44, 50, card) / 1.0E5; 463 data.nexp = getInt(50, 52, card); 464 } 465 //System.out.println(" nddot=" + data.nddot); 466 //System.out.println(" nexp=" + data.nexp); 467 468 data.bstar = getDouble(53, 59, card) / 1.0E5; 469 data.ibexp = getInt(59, 61, card); 470 //System.out.println(" bstar=" + data.bstar); 471 //System.out.println(" ibexp=" + data.ibexp); 472 473 try { 474 ephemerisType = getInt(62, 63, card); 475 //System.out.println(" ephemerisType = " + ephemerisType); 476 data.numb = ephemerisType; 477 ++ret; 478 479 elementNumber = getInt(64, 68, card); 480 //System.out.println(" elementNumber = " + elementNumber); 481 data.elnum = elementNumber; 482 ++ret; 483 } catch (Exception e) { 484 logger.error("Warning: Error Reading numb or elnum from TLE line 1 sat#:" + data.satnum); 485 } 486 487 int check = card.codePointAt(68) - 48; 488 if (check != ck1) { 489 logger.error("***** Failed checksum *****"); 490 ret = -1; 491 } 492 } 493 return ret; 494 } 495 496 private int decodeCard2(String card) { 497/* 498 System.out.println("\ndecodeCard2:"); 499 System.out.println(" card=" + card); 500 System.out.println(" length=" + card.length()); 501*/ 502 double inclination = 1.0; 503 double rightAscension = 1.0; 504 double eccentricity = 1.0; 505 double argOfPerigee = 1.0; 506 double meanAnomaly = 1.0; 507 double meanMotion = 1.0; 508 int revolutionNumber = 0; 509 510 int ret = 0; 511 //System.out.println("\n" + card); 512 int ck1 = checksum(card.substring(0, 68)); 513 String str = card.substring(0, 1); 514 if (str.equals("2")) { 515 int nsat = getInt(2, 7, card); 516 //System.out.println(" nsat = " + nsat + " data.satnum=" + data.satnum); 517 if (nsat != data.satnum) { 518 logger.error("Warning TLE line 2 Sat Num doesn't match line1 for sat: " + data.name); 519 } else { 520 inclination = getDouble(8, 16, card); 521 data.inclo = inclination; 522 //System.out.println(" inclo = " + data.inclo); 523 ++ret; 524 525 rightAscension = getDouble(17, 25, card); 526 data.nodeo = rightAscension; 527 //System.out.println(" nodeo = " + data.nodeo); 528 ++ret; 529 530 eccentricity = getDouble(26, 33, card) / 1.0E7; 531 data.ecco = eccentricity; 532 //System.out.println(" ecco = " + data.ecco); 533 ++ret; 534 535 argOfPerigee = getDouble(34, 42, card); 536 data.argpo = argOfPerigee; 537 //System.out.println(" argpo = " + data.argpo); 538 ++ret; 539 540 meanAnomaly = getDouble(43, 51, card); 541 data.mo = meanAnomaly; 542 //System.out.println(" mo = " + data.mo); 543 ++ret; 544 545 meanMotion = getDouble(52, 63, card); 546 data.no = meanMotion; 547 //System.out.println(" no = " + data.no); 548 ++ret; 549 550 try { 551 revolutionNumber = getInt(63, 68, card); 552 data.revnum = revolutionNumber; 553 //System.out.println(" revnum = " + data.revnum); 554 ++ret; 555 } catch (Exception e) { 556 logger.error("Warning: Error Reading revnum from TLE line 2 sat#:" + data.satnum + "\n" + e.toString()); 557 data.revnum = -1; 558 } 559 560 int check = card.codePointAt(68) - 48; 561 if (check != ck1) { 562 logger.error("***** Failed checksum *****"); 563 ret = -1; 564 } 565 } 566 } 567 return ret; 568 } 569 570 private int getInt(int beg, int end, String card) { 571 String str = card.substring(beg, end); 572 str = str.trim(); 573 return (new Integer(str)).intValue(); 574 } 575 576 private double getDouble(int beg, int end, String card) { 577 String str = card.substring(beg, end); 578 str = str.trim(); 579 return (new Double(str)).doubleValue(); 580 } 581 582 private int checksum(String str) { 583 int sum = 0; 584 byte[] bites = str.getBytes(); 585 for (int i=0; i<bites.length; i++) { 586 int val = (int)bites[i]; 587 if ((val > 47) && (val < 58)) { 588 sum += val - 48; 589 } else if (val == 45) { 590 ++sum; 591 } 592 } 593 return sum % 10; 594 } 595 596 protected void initDataSelectionComponents( 597 List<DataSelectionComponent> components, final DataChoice dataChoice) { 598/* 599 System.out.println("\ninitDataSelectionComponents:"); 600 System.out.println(" components=" + components); 601 System.out.println(" dataChoice=" + dataChoice); 602*/ 603 clearTimes(); 604 IntegratedDataViewer idv = getDataContext().getIdv(); 605 idv.showWaitCursor(); 606 try { 607 TimeRangeSelection timeSelection = new TimeRangeSelection(this); 608 components.add(timeSelection); 609 } catch (Exception e) { 610 logger.error("problem creating TimeRangeSelection e=" + e); 611 } 612 idv.showNormalCursor(); 613 } 614 615 /** 616 * Show the dialog 617 * 618 * @param initTabName What tab should we show. May be null. 619 * @param modal Is dialog modal 620 * 621 * @return success 622 */ 623 public boolean showPropertiesDialog(String initTabName, boolean modal) { 624 //System.out.println("\n\nshowPropertiesDialog:"); 625 boolean ret = super.showPropertiesDialog(initTabName, modal); 626 return ret; 627 } 628 629 public int getDTime() { 630 return dTime; 631 } 632 633 public void setDTime(int val) { 634 //System.out.println("PolarOrbitTrackDataSource setDTime: val=" + val); 635 dTime = val; 636 } 637}