def _build_antenna_array(self): """ Update the internal observer and antenna array information if and only if both the set_frequency and set_geometry methods have been called. """ try: assert (len(self.freq) > 0) self.site self.antennas except (AssertionError, AttributeError): return False # Convert the reference time to a JD ref_time = datetime.strptime(self.ref_time, "%Y-%m-%dT%H:%M:%S") mjd, mpm = datetime_to_mjdmpm(ref_time) ref_jd = mjd + mpm / 1000.0 / 86400 + astro.MJD_OFFSET # Create the observer and antenna array self.observer = self.site.get_observer() self.antenna_array = build_sim_array(self.site, self.antennas, self.freq / 1e9, jd=ref_jd) return True
def test_datetime(self): """Test the datetime to MJD, MPM conversion""" dt = datetime.strptime("2012-06-15 06:34:09", "%Y-%m-%d %H:%M:%S") mjd, mpm = mcs.datetime_to_mjdmpm(dt) self.assertEqual(mjd, 56093) self.assertEqual(mpm, 23649000)
def mjd(string): """ Convert a data as either a YYYY[-/]MM[-/]DD or MJD string into an integer MJD. """ try: mjd = int(string, 10) except ValueError: cstring = string.replace('-', '/') try: dt = datetime.strptime("%s 00:00:00" % cstring, "%Y/%m/%d %H:%M:%S") mjd, mpm = datetime_to_mjdmpm(dt) except ValueError: msg = "%r cannot be interpretted as an MJD or date string" % string raise ArgumentTypeError(msg) return mjd
def mpm(string): """ Covnert a time as HH:MM:SS[.SSS] or MPM string into an MPM integer. """ try: mpm = int(string, 10) if mpm < 0 or mpm > (86400 * 1000 + 999): msg = "%r is out of range for an MPM value" raise ArgumentTypeError(msg) except ValueError: try: dt = datetime.strptime("2000/1/1 %s" % string, "%Y/%m/%d %H:%M:%S.%f") except ValueError: try: dt = datetime.strptime("2000/1/1 %s" % string, "%Y/%m/%d %H:%M:%S") except ValueError: msg = "%r cannot be interpretted as a time string" % string raise ArgumentTypeError(msg) mjd, mpm = datetime_to_mjdmpm(dt) return mpm
def main(args): # Inputs if args.file is not None: mjdList = numpy.loadtxt(args.file) mjdList = mjdList.ravel() mjdList = numpy.sort(mjdList) else: tStart = "%s %s" % (args.StartDate, args.StartTime) tStop = "%s %s" % (args.StopDate, args.StopTime) # YYYY/MM/DD HH:MM:SS -> datetime instance tStart = datetime.strptime(tStart, "%Y/%m/%d %H:%M:%S.%f") tStop = datetime.strptime(tStop, "%Y/%m/%d %H:%M:%S.%f") # datetime instance to MJD mjd,mpm = datetime_to_mjdmpm(tStart) mjdStart = mjd + mpm/1000.0/86400.0 mjd,mpm = datetime_to_mjdmpm(tStop) mjdStop = mjd + mpm/1000.0/86400.0 mjdList = numpy.linspace(mjdStart, mjdStop, args.n_samples) # Setup everything for computing the position of the source if args.lwasv: site = stations.lwasv elif args.ovrolwa: site = stations.lwa1 site.lat, site.lon, site.elev = ('37.23977727', '-118.2816667', 1182.89) else: site = stations.lwa1 obs = site.get_observer() bdy = ephem.FixedBody() bdy._ra = args.RA bdy._dec = args.Dec # Setup the ionospheric model source if args.igs: mtype = 'IGS' elif args.jpl: mtype = 'JPL' elif args.code: mtype = 'CODE' elif args.ustec: mtype = 'USTEC' elif args.uqr: mtype = 'UQR' else: mtype = 'IGS' # Go! print("%-13s %-6s %-6s %-21s %-15s" % ("MJD", "Az.", "El.", "DM [pc/cm^3]", "RM [1/m^2]")) print("-"*(13+2+6+2+6+2+21+2+15)) for mjd in mjdList: # Set the date and compute the location of the target obs.date = mjd + astro.MJD_OFFSET - astro.DJD_OFFSET bdy.compute(obs) az = bdy.az*180/numpy.pi el = bdy.alt*180/numpy.pi if el > 0: # Get the latitude, longitude, and height of the ionospheric pierce # point in this direction ippLat, ippLon, ippElv = ionosphere.get_ionospheric_pierce_point(site, az, el) # Load in the TEC value and the RMS from the IGS final data product tec, rms = ionosphere.get_tec_value(mjd, lat=ippLat, lng=ippLon, include_rms=True, type=mtype) tec, rms = tec[0][0], rms[0][0] # Use the IGRF to compute the ECEF components of the Earth's magnetic field Bx, By, Bz = ionosphere.get_magnetic_field(ippLat, ippLon, ippElv, mjd=mjd, ecef=True) # Rotate the ECEF field into topocentric coordinates so that we can # get the magnetic field along the line of sight rot = numpy.array([[ numpy.sin(site.lat)*numpy.cos(site.long), numpy.sin(site.lat)*numpy.sin(site.long), -numpy.cos(site.lat)], [-numpy.sin(site.long), numpy.cos(site.long), 0 ], [ numpy.cos(site.lat)*numpy.cos(site.long), numpy.cos(site.lat)*numpy.sin(site.long), numpy.sin(site.lat)]]) ## ECEF -> SEZ sez = numpy.dot(rot, numpy.array([Bx, By, Bz])) ## SEZ -> NEZ enz = 1.0*sez[[1,0,2]] enz[1] *= -1.0 # Compute the pointing vector for this direction and use that to get # B parallel. Note that we need a negative sign when we dot to get # the direction of propagation right. pnt = numpy.array([numpy.cos(el*numpy.pi/180)*numpy.sin(az*numpy.pi/180), numpy.cos(el*numpy.pi/180)*numpy.cos(az*numpy.pi/180), numpy.sin(el*numpy.pi/180)]) Bparallel = -numpy.dot(pnt, enz) # Compute the dispersion measure and the RMS DM = 3.24078e-23 * (tec*1e16) rmsDM = 3.24078e-23 * (rms*1e16) # Compute the rotation measure and the RMS RM = 2.62e-13 * (tec*1e16) * (Bparallel*1e-9) rmsRM = 2.62e-13 * (rms*1e16) * (Bparallel*1e-9) # Report print("%013.6f %6.2f %6.2f %8.6f +/- %8.6f %5.3f +/- %5.3f" % (mjd, az, el, DM, rmsDM, RM, rmsRM)) else: # Write out dummy values since the source isn't up print("%013.6f %6.2f %6.2f %8s +/- %8s %5s +/- %5s" % (mjd, az, el, '---', '---', '---', '---'))
def main(args): obs = lwa1.get_observer() MST = pytz.timezone('US/Mountain') UTC = pytz.utc if args.date is None or args.time is None: dt = datetime.datetime.utcnow() dt = UTC.localize(dt) else: year, month, day = args.date.split('/', 2) hour, minute, second = args.time.split(':', 2) iSeconds = int(float(second)) mSeconds = int(round((float(second) - iSeconds) * 1000000)) if args.utc: # UTC dt = UTC.localize( datetime.datetime(int(year), int(month), int(day), int(hour), int(minute), iSeconds, mSeconds)) elif args.sidereal: # LST dt = astroDate(int(year), int(month), int(day), 0, 0, 0) jd = dt.to_jd() ## Get the LST in hours LST = int(hour) + int(minute) / 60.0 + (iSeconds + mSeconds / 1e6) / 3600.0 ## Get the Greenwich apparent ST for LST using the longitude of ## the site. The site longitude is stored as radians, so convert ## to hours first. GAST = LST - obs.long * 12 / math.pi ## Get the Greenwich mean ST by removing the equation of the ## equinoxes (or some approximation thereof) GMST = GAST - _getEquinoxEquation(jd) ## Get the value of D0, days since January 1, 2000 @ 12:00 UT, ## and T, the number of centuries since the year 2000. The value ## of T isn't terribly important but it is nice to include D0 = jd - 2451545.0 T = D0 / 36525.0 ## Solve for the UT hour for this LST and map onto 0 -> 24 hours ## From: http://aa.usno.navy.mil/faq/docs/GAST.php H = GMST - 6.697374558 - 0.06570982441908 * D0 - 0.000026 * T**2 H /= 1.002737909350795 while H < 0: H += 24 / 1.002737909350795 while H > 24: H -= 24 / 1.002737909350795 ## Get the full Julian Day that this corresponds to jd += H / 24.0 ## Convert the JD back to a time and extract the relevant ## quantities needed to build a datetime instance dt = astroGetDate(jd) year = dt.years month = dt.months day = dt.days hour = dt.hours minute = dt.minutes second = int(dt.seconds) microsecond = int((dt.seconds - second) * 1e6) ## Trim the microsecond down to the millisecond level microsecond = int(int(microsecond / 1000.0) * 1000) ## Localize as the appropriate time zone dt = UTC.localize( datetime.datetime(year, month, day, hour, minute, second, microsecond)) else: # Mountain time dt = MST.localize( datetime.datetime(int(year), int(month), int(day), int(hour), int(minute), iSeconds, mSeconds)) dt = dt.astimezone(UTC) obs.date = dt.astimezone(UTC).strftime("%Y/%m/%d %H:%M:%S.%f") mjd, mpm = datetime_to_mjdmpm(dt) print("Localtime: %s" % dt.astimezone(MST).strftime("%B %d, %Y at %H:%M:%S %Z")) print("UTC: %s" % dt.astimezone(UTC).strftime("%B %d, %Y at %H:%M:%S %Z")) print("LST: %s" % obs.sidereal_time()) print("MJD: %i" % mjd) print("MPM: %i" % mpm)
def main(args): # Parse the command line config = parseOptions(args) # Get LWA-1 observer = lwa1.get_observer() print("Current site is %s at lat %s, lon %s" % (lwa1.name, observer.lat, observer.long)) # Set the current time so we can find the "next" transit. Go ahead # and report the time and the current LST (for reference) if len(config['args']) == 1: config['args'][0] = config['args'][0].replace('-', '/') year, month, day = config['args'][0].split('/', 2) year = int(year) month = int(month) day = int(day) tNow = _UTC.localize(datetime(year, month, day)) else: tNow = _UTC.localize(datetime.utcnow()) observer.date = tNow.strftime("%Y/%m/%d %H:%M:%S") print("Current time is %s" % tNow.astimezone(_UTC).strftime("%Y/%m/%d %H:%M:%S %Z")) print("Current LST at %s is %s" % (lwa1.name, observer.sidereal_time())) # Load in the sources and compute srcs = [ephem.Sun(), ephem.Jupiter()] for line in _srcs: srcs.append(ephem.readdb(line)) for i in xrange(len(srcs)): srcs[i].compute(observer) # # Standard prediction output # # Header print("") print("%-10s %-23s" % ( "Source", "Next Transit", )) print("=" * (10 + 2 + 23)) # List found = False for src in srcs: if src.name.lower() == config['source'].lower(): found = True nT = str( observer.next_transit( src, start=tNow.strftime("%Y/%m/%d %H:%M:%S"))) nT = _UTC.localize(datetime.strptime(nT, "%Y/%m/%d %H:%M:%S")) print("%-10s %-23s" % (src.name, nT.strftime("%Y/%m/%d %H:%M:%S %Z"))) print("%-10s %-23s" % ("", nT.astimezone(_MST).strftime("%Y/%m/%d %H:%M:%S %Z"))) print(" ") break if found: startRec = nT - timedelta(seconds=int(round(config['duration'] / 2.0))) mjd, mpm = datetime_to_mjdmpm(startRec) dur = int(round(config['duration'] * 1000)) cmd = 'DR5 REC "%i %i %i TBN_FILT_%i"' % (mjd, mpm, dur, config['filter']) antpols = 520 sample_rate = tbn_filters[config['filter']] dataRate = 1.0 * sample_rate / 512 * tbnFRAME_SIZE * antpols print("Recording:") print(" Start: %s" % startRec.strftime("%Y/%m/%d %H:%M:%S %Z")) print(" %s" % startRec.astimezone(_MST).strftime("%Y/%m/%d %H:%M:%S %Z")) print(" Duration: %.3f s" % (dur / 1000.0, )) print(" TBN Filter Code: %i" % config['filter']) print(" Data rate: %.2f MB/s" % (dataRate / 1024**2, )) print(" Data volume: %.2f GB" % (dataRate * dur / 1000.0 / 1024**3, )) print(" ") print("Data Recorder Command:") print(" %s" % cmd) else: raise RuntimeError("Unknown source '%s'" % config['source'])
def _parse_tec_map(filename_or_fh): """ Given the name of a file containing a TEC map from the IGC, parse it and return a dictionary containing the files data. The dictionary keys are: * dates - array of MJD values for each time step in the map * lats - 2-D array of latitude values for the maps in degrees * lngs - 2-D array of longitude values for the maps in degrees * height - height for the ionospheric pierce point in km * tec - 3-D array of TEC values in TECU. The dimensions are time by latitude by longitude. * rms - 3-D array of TEC RMS values in TECU. The dimensions are time by latitude by longitude. """ # Variables to hold the map sequences dates = [] tecMaps = [] rmsMaps = [] # State control variables to help keep up with where we are inMap = False inBlock = False try: fh = gzip.GzipFile(filename_or_fh, 'rb') except TypeError: fh = gzip.GzipFile(fileobj=filename_or_fh, mode='rb') try: for line in fh: try: line = line.decode('ascii', errors='ignore') except AttributeError: pass ## Are we beginning a map? line = line.replace('\n', '') if line.find('START OF TEC MAP') != -1 or line.find( 'START OF RMS MAP') != -1: inMap = True continue ## Have we just ended a map? if line.find('END OF TEC MAP') != -1 or line.find( 'END OF RMS MAP') != -1: if line.find('TEC') != -1: tecMaps.append(cmap) else: rmsMaps.append(cmap) inMap = False continue ## Are we in a map? if inMap: ## Is this part of the preamble? if line.find('EPOCH OF CURRENT MAP') != -1: ### Parse the date/time string year, month, day, hour, minute, second = line.split( None, 6)[:6] year = int(year) month = int(month) day = int(day) hour = int(hour) minute = int(minute) second = int(second) ### Figure out the MJD try: dt = datetime(year, month, day, hour, minute, second, 0) except ValueError: if hour >= 24: dt = datetime(year, month, day, hour - 24, minute, second, 0) dt += timedelta(days=1) else: continue mjd, mpm = datetime_to_mjdmpm(dt) mjd = mjd + mpm / 1000.0 / 3600.0 / 24.0 if mjd not in dates: dates.append(mjd) ### Initialize the map and the coorindates cmap = [] lats = [] lngs = [] continue ## Is this a different part of the preamble? elif line.find('LAT/LON1/LON2/DLON/H') != -1: lat = float(line[3:8]) lng1 = float(line[8:14]) lng2 = float(line[14:20]) dlng = float(line[20:26]) height = float(line[26:32]) cmap.append([]) lats.append(lat) lngs = list(numpy.arange(lng1, lng2 + dlng, dlng)) inBlock = True continue ## Process the data block keeping in mind that missing values are stored ## as 9999 if inBlock: fields = numpy.array( [float(v) / 10.0 for v in line.split(None)]) fields[numpy.where(fields == 999.9)] = numpy.nan cmap[-1].extend(fields) continue finally: fh.close() # Combine everything together dates = numpy.array(dates, dtype=numpy.float64) tec = numpy.array(tecMaps, dtype=numpy.float32) rms = numpy.array(rmsMaps, dtype=numpy.float32) lats = numpy.array(lats, dtype=numpy.float32) lngs = numpy.array(lngs, dtype=numpy.float32) # Do we have a valid RMS map? If not, make one. if rms.size != tec.size: rms = tec * 0.05 # Make lats and lngs 2-D to match the data lngs, lats = numpy.meshgrid(lngs, lats) # Build up the output output = { 'dates': dates, 'lats': lats, 'lngs': lngs, 'height': height, 'tec': tec, 'rms': rms } # Done return output
def get_magnetic_field(lat, lng, elev, mjd=None, ecef=False): """ Given a geodetic location described by a latitude in degrees (North positive), a longitude in degrees (West negative), an elevation in meters and an MJD value, compute the Earth's magnetic field in that location and return a three-element tuple of the magnetic field's components in nT. By default these are in topocentric coordinates of (North, East, Up). To return values in ECEF, set the 'ecef' keyword to True. If the MJD file is None, the current time is used. .. note:: The convention used for the topocentric coordinates differs from what the IGRF uses in the sense that the zenith direction points up rather than down. """ # Get the current time if mjd is None if mjd is None: mjd, mpm = datetime_to_mjdmpm(datetime.utcnow()) mjd = mjd + mpm / 1000.0 / 3600.0 / 24.0 # Convert the MJD to a decimal year. This is a bit tricky ## Break the MJD into an integer MJD and an MPM in order to build a datetime instance mpm = int((mjd - int(mjd)) * 24.0 * 3600.0 * 1000.0) mjd0 = mjdmpm_to_datetime(int(mjd), mpm) ## Convert the datetime instance to January 1 mjd0 = mjd0.replace(month=1, day=1, hour=0, second=0, microsecond=0) ## Figure out January 1 for the following year mjd1 = mjd0.replace(year=mjd0.year + 1) ## Figure out how long the year is in days diffDays = mjd1 - mjd0 diffDays = diffDays.days + diffDays.seconds / 86400.0 + diffDays.microseconds / 1e6 / 86400.0 ## Convert the January 1 date back to an MJD mjd0, mpm0 = datetime_to_mjdmpm(mjd0) mjd0 = mjd0 + mpm / 1000.0 / 3600.0 / 24.0 year = (mjd1.year - 1) + (mjd - mjd0) / diffDays # Convert the geodetic position provided to a geocentric one for calculation ## Deal with the poles if 90.0 - lat < 0.001: xyz = numpy.array( geo_to_ecef(89.999 * numpy.pi / 180, lng * numpy.pi / 180, elev)) elif 90.0 + lat < 0.001: xyz = numpy.array( geo_to_ecef(-89.999 * numpy.pi / 180, lng * numpy.pi / 180, elev)) else: xyz = numpy.array( geo_to_ecef(lat * numpy.pi / 180, lng * numpy.pi / 180, elev)) ## To geocentric r = numpy.sqrt((xyz**2).sum()) lt = numpy.arcsin(xyz[2] / r) ln = numpy.arctan2(xyz[1], xyz[0]) # Load in the coefficients try: coeffs = _ONLINE_CACHE['IGRF'] except KeyError: filename = os.path.join(dataPath, 'igrf13coeffs.txt') _ONLINE_CACHE['IGRF'] = _load_igrf(filename) coeffs = _ONLINE_CACHE['IGRF'] # Compute the coefficients for the epoch coeffs = _compute_igrf_coefficents(year, coeffs) # Compute the field strength in spherical coordinates Br, Bth, Bph = 0.0, 0.0, 0.0 for n in coeffs['g'].keys(): for m in range(0, n + 1): Br += (n + 1.0) * (_RADIUS_EARTH / r)**(n + 2) * _Snm( n, m) * coeffs['g'][n][m] * numpy.cos(m * ln) * _Pnm( n, m, numpy.sin(lt)) Br += (n + 1.0) * (_RADIUS_EARTH / r)**(n + 2) * _Snm( n, m) * coeffs['h'][n][m] * numpy.sin(m * ln) * _Pnm( n, m, numpy.sin(lt)) Bth -= (_RADIUS_EARTH / r)**(n + 2) * _Snm( n, m) * coeffs['g'][n][m] * numpy.cos(m * ln) * _dPnm( n, m, numpy.sin(lt)) Bth -= (_RADIUS_EARTH / r)**(n + 2) * _Snm( n, m) * coeffs['h'][n][m] * numpy.sin(m * ln) * _dPnm( n, m, numpy.sin(lt)) Bph += (_RADIUS_EARTH / r)**(n + 2) / numpy.cos(lt) * _Snm( n, m) * coeffs['g'][n][m] * m * numpy.sin(m * ln) * _Pnm( n, m, numpy.sin(lt)) Bph -= (_RADIUS_EARTH / r)**(n + 2) / numpy.cos(lt) * _Snm( n, m) * coeffs['h'][n][m] * m * numpy.cos(m * ln) * _Pnm( n, m, numpy.sin(lt)) ## And deal with NaNs if numpy.isnan(Br): Br = 0.0 if numpy.isnan(Bth): Bth = 0.0 if numpy.isnan(Bph): Bph = 0.0 # Convert from spherical to ECEF Bx = Br * numpy.cos(lt) * numpy.cos(ln) + Bth * numpy.sin(lt) * numpy.cos( ln) - Bph * numpy.sin(ln) By = Br * numpy.cos(lt) * numpy.sin(ln) + Bth * numpy.sin(lt) * numpy.sin( ln) + Bph * numpy.cos(ln) Bz = Br * numpy.sin(lt) - Bth * numpy.cos(lt) # Are we done? if ecef: # For ECEF we don't need to do anything else outputField = Bx, By, Bz else: # Convert from ECEF to topocentric (geodetic) ## Update the coordinates for geodetic lt = lat * numpy.pi / 180.0 if 90.0 - lat < 0.001: lt = 89.999 * numpy.pi / 180.0 elif 90.0 + lat < 0.001: lt = -89.999 * numpy.pi / 180.0 else: lt = lat * numpy.pi / 180.0 ln = lng * numpy.pi / 180.0 ## Build the rotation matrix for ECEF to SEZ rot = numpy.array([[ numpy.sin(lt) * numpy.cos(ln), numpy.sin(lt) * numpy.sin(ln), -numpy.cos(lt) ], [-numpy.sin(ln), numpy.cos(ln), 0], [ numpy.cos(lt) * numpy.cos(ln), numpy.cos(lt) * numpy.sin(ln), numpy.sin(lt) ]]) ## Apply and extract sez = numpy.dot(rot, numpy.array([Bx, By, Bz])) Bn, Be, Bz = -sez[0], sez[1], sez[2] outputField = Bn, Be, Bz # Done return outputField
def _parse_ustec_map(filename_or_fh): """ Given the name of a file containing a TEC map from the USTEC project, parse it and return a dictionary containing the files data. The dictionary keys are: * dates - array of MJD values for each time step in the map * lats - 2-D array of latitude values for the maps in degrees * lngs - 2-D array of longitude values for the maps in degrees * height - height for the ionospheric pierce point in km * tec - 3-D array of TEC values in TECU. The dimensions are time by latitude by longitude. * rms - 3-D array of TEC RMS values in TECU. The dimensions are time by latitude by longitude. """ try: tf = tarfile.open(filename_or_fh, 'r:*') do_close = True except TypeError: tf = tarfile.open(fileobj=filename_or_fh, mode='r:*') do_close = False valid_filename = lambda x: ((x.find('_TEC.txt') != -1) \ or (x.find('_ERR.txt') != -1) \ or (x.find('_EOF.txt') != -1)) try: tecFiles = {} errFiles = {} eofFiles = {} for entry in tf: if not valid_filename(entry.name): continue contents = tf.extractfile(entry.name).read() try: contents = contents.decode() except AttributeError: # Python2 catch pass contents = StringIO(contents) if entry.name.find('_TEC.txt') != -1: tecFiles[entry.name] = contents elif entry.name.find('_ERR.txt') != -1: errFiles[entry.name] = contents elif entry.name.find('_EOF.txt') != -1: eofFiles[entry.name] = contents # Variables to hold the map sequences dates = [] tecMaps = [] rmsMaps = [] # Get all of the TEC map files and load them in tecfilenames = list(tecFiles.keys()) tecfilenames.sort() for tecfilename in tecfilenames: tecfh = tecFiles[tecfilename] try: rmsfilename = tecfilename.replace('_TEC', '_ERR') rmsfh = errFiles[rmsfilename] except KeyError: rmsfh = None lats, lngs, tec, rms = _parse_ustec_individual(tecfh, rmsname_or_fh=rmsfh) ### Figure out the MJD dt = os.path.basename(tecfilename).split('_')[0] dt = datetime.strptime(dt, "%Y%m%d%H%M") mjd, mpm = datetime_to_mjdmpm(dt) mjd = mjd + mpm / 1000.0 / 3600.0 / 24.0 if mjd not in dates: dates.append(mjd) # Stack on the new TEC and RMS maps tecMaps.append(tec) rmsMaps.append(rms) #except Exception as e: #pass # Get the mean ionospheric height eoffilename = list(eofFiles.keys())[0] #try: height = _parse_ustec_height(eofFiles[eoffilename]) #except: # height = 450 finally: if do_close: tf.close() # Combine everything together dates = numpy.array(dates, dtype=numpy.float64) tec = numpy.array(tecMaps, dtype=numpy.float32) rms = numpy.array(rmsMaps, dtype=numpy.float32) # Build up the output output = { 'dates': dates, 'lats': lats, 'lngs': lngs, 'height': height, 'tec': tec, 'rms': rms } # Done return output
def time(self): """ Function to convert the time tag to seconds since the UNIX epoch as a `lsl.reader.base.FrameTimestamp` instance. """ # Get the reference epoch in the strange way that it is stored in VDIF # and convert it to a MJD epochDT = datetime(2000 + self.ref_epoch // 2, (self.ref_epoch % 2) * 6 + 1, 1, 0, 0, 0, 0) epochMJD, epochMPM = datetime_to_mjdmpm(epochDT) epochMJD = epochMJD + epochMPM / 1000.0 / 86400.0 # Get the frame MJD by adding the seconds_from_epoch value to the epoch frameMJD_i = epochMJD + self.seconds_from_epoch // 86400 frameMJD_f = (self.seconds_from_epoch % 86400) / 86400.0 frameMJD_s = 0.0 if self.sample_rate == 0.0: # Try to get the sub-second time by parsing the extended user data try: ## Is there a sample rate to grab? eud = self.extended_user_data sample_rate = eud['sample_rate'] sample_rate *= 1e6 if eud['sample_rate_units'] == 'MHz' else 1e3 ## How many samples are in each frame? dataSize = self.frame_length * 8 - 32 + 16 * self.is_legacy # 8-byte chunks -> bytes - full header + legacy offset samplesPerWord = 32 // self.bits_per_sample # dimensionless nSamples = dataSize // 4 * samplesPerWord # bytes -> words -> data samples nSamples = nSamples / self.nchan / ( 2 if self.is_complex else 1 ) # data samples -> time samples ## What is the frame rate? frameRate = sample_rate // nSamples frameMJD_s += 1.0 * self.frame_in_second / frameRate except KeyError: warnings.warn( colorfy( "{{%yellow Insufficient information to determine exact frame timestamp, time will be approximate" ), RuntimeWarning) else: # Use what we already have been told ## How many samples are in each frame? dataSize = self.frame_length * 8 - 32 + 16 * self.is_legacy # 8-byte chunks -> bytes - full header + legacy offset samplesPerWord = 32 // self.bits_per_sample # dimensionless nSamples = dataSize // 4 * samplesPerWord # bytes -> words -> samples nSamples = nSamples // self.nchan // ( 2 if self.is_complex else 1) # data samples -> time samples ## What is the frame rate? frameRate = self.sample_rate // nSamples frameMJD_s += 1.0 * self.frame_in_second / frameRate # Convert from MJD to UNIX time if frameMJD_f > 1: frameMJD_i += 1 frameMJD_f -= 1 return FrameTimestamp.from_pulsar_mjd(frameMJD_i, frameMJD_f, frameMJD_s)
def _parse_ustec_map(filename): """ Given the name of a file containing a TEC map from the USTEC project, parse it and return a dictionary containing the files data. The dictionary keys are: * dates - array of MJD values for each time step in the map * lats - 2-D array of latitude values for the maps in degrees * lngs - 2-D array of longitude values for the maps in degrees * height - height for the ionospheric pierce point in km * tec - 3-D array of TEC values in TECU. The dimensions are time by latitude by longitude. * rms - 3-D array of TEC RMS values in TECU. The dimensions are time by latitude by longitude. """ tempDir = tempfile.mkdtemp(prefix='ionosphere-') tf = tarfile.open(filename, 'r:*') tecFiles = [ tio for tio in tf.getmembers() if tio.name.find('_TEC.txt') != -1 ] errFiles = [ tio for tio in tf.getmembers() if tio.name.find('_ERR.txt') != -1 ] eofFiles = [ tio for tio in tf.getmembers() if tio.name.find('_EOF.txt') != -1 ] tf.extractall(path=tempDir, members=tecFiles) tf.extractall(path=tempDir, members=errFiles) tf.extractall(path=tempDir, members=eofFiles) # Variables to hold the map sequences dates = [] tecMaps = [] rmsMaps = [] # Get all of the TEC map files and load them in tecfilenames = glob.glob(os.path.join(tempDir, '*_TEC.txt')) tecfilenames.sort() for tecfilename in tecfilenames: #try: dt, lats, lngs, tec, rms = _parse_ustec_individual(tecfilename) ### Figure out the MJD mjd, mpm = datetime_to_mjdmpm(dt) mjd = mjd + mpm / 1000.0 / 3600.0 / 24.0 if mjd not in dates: dates.append(mjd) # Stack on the new TEC and RMS maps tecMaps.append(tec) rmsMaps.append(rms) #except Exception as e: #pass # Get the mean ionospheric height eoffilename = glob.glob(os.path.join(tempDir, '*_EOF.txt'))[0] #try: height = _parse_ustec_height(eoffilename) #except: # height = 450 # Cleanup tf.close() shutil.rmtree(tempDir, ignore_errors=True) # Combine everything together dates = numpy.array(dates, dtype=numpy.float64) tec = numpy.array(tecMaps, dtype=numpy.float32) rms = numpy.array(rmsMaps, dtype=numpy.float32) # Build up the output output = { 'dates': dates, 'lats': lats, 'lngs': lngs, 'height': height, 'tec': tec, 'rms': rms } # Done return output