def test_colorfy(self): """Test colorfy""" color.colorfy("This is a plain string") color.colorfy("This is a {{%red string}}") color.colorfy("This is an {{%underline string}}") color.colorfy("This is an {{%yellow {{%underline string}}}}")
def show(self): """ Build a string representation of the progress bar and return it. """ if self.print_percent: # If we want the percentage also displayed, trim a little # more from the progress bar's wdith barSpan = self.span - 9 nMarks = float(self.amount) / self.max * barSpan nMarksFull = int(nMarks) if nMarksFull < barSpan: partial = nMarks - nMarksFull lastMark = self.rotations[int(partial * len(self.rotations))] else: lastMark = '' bar = self.sym * nMarksFull bar = bar + lastMark bar = bar + (' ' * (barSpan - (nMarksFull + len(lastMark)))) nte = "%5.1f%%" % (float(self.amount) / self.max * 100) if self.color is None: out = "[%s] %s" % (bar, nte) else: out = colorfy("[{{%%%s %s}}] %s" % (self.color, bar, nte)) else: # Progress bar only barSpan = self.span - 2 nMarks = float(self.amount) / self.max * barSpan nMarksFull = int(nMarks) if nMarksFull < barSpan: partial = nMarks - nMarksFull lastMark = self.rotations[int(partial * len(self.rotations))] else: lastMark = '' bar = self.sym * nMarksFull bar = bar + lastMark bar = bar + (' ' * (barSpan - (nMarksFull + len(lastMark)))) if self.color is None: out = "[%s]" % bar else: out = colorfy("[{{%%%s %s}}]" % (self.color, bar)) return out
def downsample(vector, factor, rescale=True): """ Downsample (i.e. co-add consecutive numbers) a vector by an integer factor. Trims the input timeseries to be a multiple of the downsample factor, if needed. If rescale == True, then divides each sum by factor to produce a mean value, otherwise just adds the values in the vector. """ if (len(vector) % factor): warnings.warn(colorfy("{{%%yellow Length of 'vector' is not divisible by 'factor'=%d, clipping!}}" % factor), RuntimeWarning) newlen = (len(vector)//factor)*factor warnings.warn(colorfy("{{%%yellow Oldlen %d, newlen %d}}" % (len(vector), newlen)), RuntimeWarning) vector = vector[:newlen] if rescale: newvector = numpy.reshape(vector, (len(vector)//factor, factor))/float(factor) else: newvector = numpy.reshape(vector, (len(vector)//factor, factor)) return numpy.add.reduce(newvector, 1)
def _download_worker_standard(url, filename): """ Download the URL and save it to a file. """ # Attempt to download the data print("Downloading %s" % url) try: tecFH = urlopen(url, timeout=DOWN_CONFIG.get('timeout')) meta = tecFH.info() try: remote_size = int(meta.getheaders("Content-Length")[0]) except AttributeError: remote_size = 1 pbar = DownloadBar(max=remote_size) while True: new_data = tecFH.read(DOWN_CONFIG.get('block_size')) if len(new_data) == 0: break pbar.inc(len(new_data)) try: data += new_data except NameError: data = new_data sys.stdout.write(pbar.show() + '\r') sys.stdout.flush() tecFH.close() sys.stdout.write(pbar.show() + '\n') sys.stdout.flush() except IOError as e: warnings.warn( colorfy("{{%%yellow Error downloading file from %s: %s}}" % (url, str(e))), RuntimeWarning) data = '' except socket.timeout: data = '' # Did we get anything or, at least, enough of something like it looks like # a real file? if len(data) < 3: ## Fail return False else: ## Success! Save it to a file with _CACHE_DIR.open(filename, 'wb') as fh: fh.write(data) ## Further processing, if needed if os.path.splitext(filename)[1] == '.Z': ## Save it to a regular gzip'd file after uncompressing it. _convert_to_gzip(filename) return True
def _render_flow(self, active=True): out = [ ' ', ] * self.width for i in (-2, -1, 0): out[self._i + i] = '>' out = ''.join(out) if self.color is not None: out = colorfy("{{%%%s %s}}" % (self.color, out)) self._i += 1 self._i %= self.width return out
def _regrid_linear(x, y, newx, allow_extrapolation=False): """ Implement regrid() function using linear interpolation. """ if allow_extrapolation: warnings.warn(colorfy("{{%yellow allow_extrapolation=True not honored for regrid_linear"), RuntimeWarning) if newx.min() < x.min(): raise ValueError('x.min(%f) must be smaller than newx.min(%f)' % (x.min(), newx.min())) if newx.max() > x.max(): raise ValueError('x.max(%f) must be larger than newx.max(%f)' % (x.max(), newx.max())) return numpy.interp(newx, x, y)
def _render_boomerang(self, active=True): out = [ ' ', ] * self.width out[self._i] = ('|', '/', '-', '\\')[self._i % 4] out = ''.join(out) if self.color is not None: out = colorfy("{{%%%s %s}}" % (self.color, out)) self._i += self._dir if self._i < 0: self._i += 1 self._dir = 1 if self._i == self.width: self._i -= 2 self._dir = -1 return out
def _run(self): """ Internal function used by the thread to make/change the displayed text. """ while self.alive.isSet(): if self.color is None: out = "%s%s%s\r" % (self.message, '.' * self._i, ' ' * (self.dots - self._i)) else: out = colorfy("%s{{%%%s %s}}%s\r" % (self.message, self.color, '.' * self._i, ' ' * (self.dots - self._i))) sys.stdout.write(out) sys.stdout.flush() self._i += 1 self._i %= (self.dots + 1) time.sleep(self.interval)
def read_guppi_header(filehandle): """ Read in a GUPPI header at the start of a VDIF file from the VLA. The contents of the header are returned as a dictionary. """ # Is there a GUPPI header? header = {} if not has_guppi_header(filehandle): warnings.warn( colorfy( "{{%yellow GUPPI header not found, returning an empty dictionary}}" ), RuntimeWarning) return header # Read in the GUPPI header while True: line = filehandle.read(80) try: line = line.decode(encoding='ascii', errors='ignore') except AttributeError: pass if line[:3] == 'END': break elif line[:8] == 'CONTINUE': junk, value2 = line.split(None, 1) value = "%s%s" % (value[:-1], value2[:-1]) else: name, value = line.split('=', 1) name = name.strip() try: value = int(value, 10) except ValueError: try: value = float(value) except ValueError: value = value.strip().replace("'", '') header[name.strip()] = value header['OBSBW'] *= 1e6 header['OBSFREQ'] *= 1e6 return header
def is_valid(tarname, verbose=False): """ Given a filename, see if it is valid metadata tarball or not. .. versionadded:: 1.2.0 """ passes = 0 failures = 0 try: get_session_spec(tarname) passes += 1 if verbose: print(colorfy("Session specification - {{%green OK}}")) except IOError as e: raise e except: failures += 1 if verbose: print(colorfy("Session specification - {{%red {{%bold FAILED}}}}")) try: get_observation_spec(tarname) passes += 1 if verbose: print(colorfy("Observation specification(s) - {{%green OK}}")) except: failures += 1 if verbose: print( colorfy( "Observation specification(s) - {{%red {{%bold FAILED}}}}") ) try: get_command_script(tarname) passes += 1 if verbose: print(colorfy("Command script - {{%green OK}}")) except: failures += 1 if verbose: print(colorfy("Command script - {{%red {{%bold FAILED}}}}")) if verbose: print("---") print("%i passed / %i failed" % (passes, failures)) return False if failures else True
def _render_pingpong(self, active=True): sym = 'o' if active else '.' out = '' if self._i == 0: out += ")%s" % sym out += ' ' * (self.width - 3) out += '|' self._dir = 1 elif self._i == self.width - 3: out += '|' out += ' ' * (self.width - 3) out += "%s(" % sym self._dir = -1 else: out += '|' out += ' ' * (self._i) out += sym out += ' ' * (self.width - self._i - 3) out += '|' if self.color is not None: out = colorfy(out.replace(sym, "{{%%%s %s}}" % (self.color, sym))) self._i += self._dir return out
def stop(self, success=True): """ Stop the indicator and display a 'Done' or 'Failed' message depending on whether or not the 'success' keyword is True. .. note:: This can take up to one BusyIndicator.interval to complete. """ if self.thread is not None: if self.color is None: out = "%s%s%s%s\n" % (self.message, '.' * self._i, 'Done' if success else 'Failed', ' ' * self.dots) else: out = colorfy( "%s{{%%%s %s}}%s%s\n" % (self.message, self.color, '.' * self._i, 'Done' if success else 'Failed', ' ' * self.dots)) sys.stdout.write(out) self.alive.clear() self.thread.join() self.thread = None self._i = 0
def build_sim_array(station, antennas, freq, jd=None, pos_error=0.0, force_flat=False, force_gaussian=False, verbose=False): """ Build a AIPY AntennaArray for simulation purposes. Inputs are a station object defined from the lwa_common module, a numpy array of stand numbers, and a numpy array of frequencies in either Hz of GHz. Optional inputs are a Julian Date to set the array to and a positional error terms that perturbs each of the stands in x, y, and z. The output of this module is an AIPY AntennaArray object. The shape of the antenna response is either flat (gain of 1 in all directions), modeled by a 2-D Gaussian with the specified full width at half maximum in degrees, or modeled by a collection of spherical harmonics that are polynomials in frequency. The spherical harmonics are used if the file 'beam_shape.npz' is found in the current directory. .. versionchanged:: 1.0.3 Changed the meaning of the force_gaussian parameters so that the Gaussian full width at half maximum in degrees is passed in. .. versionchanged:: 1.0.1 Moved the simulation code over from AIPY to the new _simFast module. This should be much faster but under the caveats that the bandpass and antenna gain patterns are the same for all antennas. This should be a reasonable assumption for large-N arrays. Added an option to use a 2-D Gaussian beam pattern via the force_gaussian keyword. .. versionchanged:: 0.4.0 Switched over to passing in Antenna instances generated by the :mod:`lsl.common.station` module instead of a list of stand ID numbers. """ # If the frequencies are in Hz, we need to convert to GHz try: freqs = freq.copy() except AttributeError: freqs = numpy.array(freq) if freqs.shape == (): freqs.shape = (1, ) if freqs.min() > 1e6: freqs /= 1.0e9 # If the beam Alm coefficient file is present, build a more realistic beam # response. Otherwise, assume a flat beam if force_gaussian: try: xw, yw = force_gaussian xw, yw = float(xw), float(yw) except (TypeError, ValueError) as e: xw = float(force_gaussian) yw = 1.0 * xw # FWHM to sigma xw /= 2.0 * numpy.sqrt(2.0 * numpy.log(2.0)) yw /= 2.0 * numpy.sqrt(2.0 * numpy.log(2.0)) # Degrees to radians xw *= numpy.pi / 180 yw *= numpy.pi / 180 if verbose: print( "Using a 2-D Gaussian beam with sigmas %.1f by %.1f degrees" % (xw * 180 / numpy.pi, yw * 180 / numpy.pi)) beam = Beam2DGaussian(freqs, xw, yw) elif force_flat: if verbose: print("Using flat beam model") beam = Beam(freqs) else: if os.path.exists(os.path.join(dataPath, 'beam-shape.npz')): dd = numpy.load(os.path.join(dataPath, 'beam-shape.npz')) coeffs = dd['coeffs'] deg = coeffs.shape[0] - 1 lmax = int((math.sqrt(1 + 8 * coeffs.shape[1]) - 3) / 2) beamShapeDict = {} for i in range(deg + 1): beamShapeDict[i] = numpy.squeeze(coeffs[-1 - i, :]) try: dd.close() except AttributeError: pass if verbose: print( "Using Alm beam model with %i-order freq. polynomial and %i-order sph. harmonics" % (deg, lmax)) beam = BeamAlm(freqs, lmax=lmax, mmax=lmax, deg=deg, nside=128, coeffs=beamShapeDict) else: if verbose: print("Using flat beam model") beam = Beam(freqs) if pos_error != 0: warnings.warn( colorfy( "{{%%yellow Creating array with positional errors between %.3f and %.3f m}}" % (-pos_error, pos_error)), RuntimeWarning) # Build an array of AIPY Antenna objects ants = [] for antenna in antennas: top = numpy.array([antenna.stand.x, antenna.stand.y, antenna.stand.z]) top += (2 * pos_error * numpy.random.rand(3) - pos_error ) # apply a random positional error if needed top.shape = (3, ) eq = numpy.dot(aipy.coord.top2eq_m(0.0, station.lat), top) eq /= speedOfLight # m -> s eq *= 1e9 # s -> ns delayCoeff = numpy.zeros(2) amp = 0 * antenna.cable.gain(freqs * 1e9) + 1 ants.append( Antenna(eq[0], eq[1], eq[2], beam, phsoff=delayCoeff, amp=amp, stand=antenna.stand.id)) # Combine the array of antennas with the array's location to generate an # AIPY AntennaArray object simAA = AntennaArray(station.aipy_location, ants) simAA._station = station # Set the Julian Data for the AntennaArray object if it is provided. The try...except # clause is used to deal with people who may want to pass an array of JDs in rather than # just one. If one isn't provided, use the date set for the input 'station'. if jd is None: simAA.date = station.date else: try: simAA.set_jultime(jd[0]) except TypeError: simAA.set_jultime(jd) return simAA
def __init__(self, necname, freq, rerun=True): # Modify NEC file to set FR card to use "freq" # Run NEC if necessary. # anntenna_pat_dB[az,alt] is the total gain or current in dB in the # direction az, alt (integer degrees), where az (azimuth) runs from # 0 to 359, where 0 is North and alt (altitude) runs from 0 to 89 , # where 0 is the horizon The default pattern is all zeros (isotropic # response) self.antenna_pat_dB = zeros(shape=(360, 90), dtype=float32) self.antenna_pat_complex = zeros(shape=(360, 90), dtype=complex64) outname = os.path.splitext(necname)[0] + '.out' try: fh, filefreq = open_and_get_nec_freq(outname) except: warnings.warn( colorfy("{{%yellow NEC .out file not found! Running NEC}}"), RuntimeWarning) fh = None if fh is None or not close_to(filefreq, freq): if rerun: warnings.warn( colorfy( "{{%yellow NEC output file is at a different frequency \ than the requested frequency: re-running}}"), RuntimeWarning) if fh is not None: fh.close() change_nec_freq(necname, freq) # Make sure we have NEC install if which_nec4() is None: raise RuntimeError( "NEC executable 'nec4d' not found in PATH") # Important NOTE: # This requires a modified version of NEC-4 that # takes 2 command line arguments instead of asking questions # interactively. See Paul Ray for info. try: subprocess.check_call(['nec4d', necname, outname]) except subprocess.CalledProcessError as e: raise RuntimeError( "Bad return value from nec2++ call : %e" % str(e)) fh, filefreq = open_and_get_nec_freq(outname) if not close_to(filefreq, freq): fh.close() raise ValueError( "NEC failed to generate a file with the correct frequency." ) else: raise ValueError("NEC output file is at a different frequency (%f) than the requested frequency (%f)." % \ (filefreq, freq)) # Now look for RADIATION PATTERN or EXCITATION and read it radpat = None for line in fh: if line.find('RADIATION PATTERN') >= 0: radpat = True break if line.find('EXCITATION') >= 0: radpat = False break else: raise RuntimeError("RADIATION PATTERN nor EXCITATION not found!") if radpat: self._read_radiation(fh) else: self._read_excitation(fh) fh.close()
def show(self): """ Build a string representation of the download bar and return it. """ if self.t0 is None: # Have we started? cte = '----- B/s' elif self.amount == 0: # Have we gone far enough to get a "good" estimate? cte = '----- B/s' elif self.amount >= self.max: # Are we done? cte = self._pprint(self.amount)[:-2] elif self.t1 - self.t0 < 0.01: # Have we running long enough to get a "good" estimate? cte = '----- B/s' else: cte = self.amount / (self.t1 - self.t0) cte = self._pprint(cte) if self.print_percent: # If we want the percentage also displayed, trim a little # more from the progress bar's wdith barSpan = self.span - 10 nMarks = float(self.amount) / self.max * barSpan nMarksFull = min([int(nMarks), barSpan]) if nMarksFull < barSpan: partial = nMarks - nMarksFull lastMark = self.rotations[int(partial * len(self.rotations))] else: lastMark = '' bar = self.sym * nMarksFull bar = bar + lastMark bar = bar + (' ' * (barSpan - (nMarksFull + len(lastMark)))) nte = "%5.1f%%" % (float(self.amount) / self.max * 100) if self.amount > self.max: nte = "-----%" if self.color is None: out = "[%s] %s %s" % (bar, nte, cte) else: out = colorfy("[{{%%%s %s}}] %s %s" % (self.color, bar, nte, cte)) else: # Progress bar only barSpan = self.span - 3 nMarks = float(self.amount) / self.max * barSpan nMarksFull = int(nMarks) if nMarksFull < barSpan: partial = nMarks - nMarksFull lastMark = self.rotations[int(partial * len(self.rotations))] else: lastMark = '' bar = self.sym * nMarksFull bar = bar + lastMark bar = bar + (' ' * (barSpan - (nMarksFull + len(lastMark)))) if self.color is None: out = "[%s] %s" % (bar, cte) else: out = colorfy("[{{%%%s %s}}] %s" % (self.color, bar, cte)) return out
'get_magnetic_field', 'compute_magnetic_declination', 'compute_magnetic_inclination', 'get_tec_value', 'get_ionospheric_pierce_point' ] # Create the cache directory try: _CACHE_DIR = FileCache(os.path.join(os.path.expanduser('~'), '.lsl', 'ionospheric_cache'), max_size=lambda: IONO_CONFIG.get('max_cache_size')) except OSError: _CACHE_DIR = MemoryCache( max_size=lambda: IONO_CONFIG.get('max_cache_size')) warnings.warn( colorfy( "{{%yellow Cannot create or write to on-disk data cache, using in-memory data cache}}" ), RuntimeWarning) # Create the on-line cache _ONLINE_CACHE = {} # Radius of the Earth in meters for the IGRF _RADIUS_EARTH = 6371.2 * 1e3 def _load_igrf(filename): """ Given a filename pointing to a list of IGRF coefficients, load in the data and return a dictionary containing the raw coefficients. The dictionary keys are:
def _write_singledish_hdu(self): """ Define the SINGLE DISH table. """ scanList = [] dateList = [] timeList = [] intTimeList = [] beamList = [] mList = [] rawList = [] scanCount = 1 for i, dataSet in enumerate(self.data): if dataSet.pol == self.stokes[0]: tempMList = {} for stokes in self.stokes: tempMList[stokes] = {} beams = list(dataSet.dataDict.keys()) beams.sort() for b in beams: specData = dataSet.dataDict[b] # Load the data into a matrix tempMList[dataSet.pol][b] = specData.ravel() if dataSet.pol == self.stokes[0]: # Observation date and time utc = astro.taimjd_to_utcjd(dataSet.obsTime) date = astro.get_date(utc) date.hours = 0 date.minutes = 0 date.seconds = 0 utc0 = date.to_jd() scanList.append(scanCount) dateList.append('%4i-%02i-%02i' % (date.years, date.months, date.days)) timeList.append((utc - utc0) * 24 * 3600) intTimeList.append(dataSet.intTime) beamList.append(b.id) rawList.append(b) if dataSet.pol == self.stokes[-1]: for b in rawList: matrix = numpy.zeros((self.nStokes, self.nChan), dtype=numpy.float32) for p in range(self.nStokes): try: matrix[p, :] = tempMList[self.stokes[p]][b] except KeyError: warnings.warn( colorfy( "{{%%yellow Key mis-match %s %s}}" % (str(b), str(tempMList[self.stokes[p]].keys()))), RuntimeWarning) mList.append(matrix.ravel()) scanCount += 1 rawList = [] # Scan number c1 = astrofits.Column(name='SCAN', format='1I', array=numpy.array(scanList)) ## Cycle #c2 = astrofits.Column(name='CYCLE', format='1J', #array=numpy.array([1,]*len(scanList))) # DATE-OBS c3 = astrofits.Column(name='DATE-OBS', format='10A', array=numpy.array(dateList)) # Time elapsed since 0h c4 = astrofits.Column(name='TIME', format='1D', unit='s', array=numpy.array(timeList)) # Integration time (seconds) c5 = astrofits.Column(name='EXPOSURE', format='1E', unit='s', array=numpy.array(intTimeList, dtype=numpy.float32)) # Object name c6 = astrofits.Column(name='OBJECT', format='16A', array=numpy.array([ 'LWA_OBS', ] * len(scanList))) # Object position (deg and deg) c7 = astrofits.Column(name='OBJ-RA', format='1D', unit='deg', array=numpy.array([ 0.0, ] * len(scanList))) c8 = astrofits.Column(name='OBJ-DEC', format='1D', unit='deg', array=numpy.array([ 0.0, ] * len(scanList))) # Rest frequency (Hz) c9 = astrofits.Column(name='RESTFRQ', format='1D', unit='Hz', array=numpy.array([ 0.0, ] * len(scanList))) # Observation mode c10 = astrofits.Column(name='OBSMODE', format='16A', array=numpy.array([ self.mode, ] * len(scanList))) # Beam (tuning) c11 = astrofits.Column(name='BEAM', format='1I', array=numpy.array(beamList)) # IF c12 = astrofits.Column(name='IF', format='1I', array=numpy.array([ self.freq[0].id, ] * len(scanList))) # Frequency resolution (Hz) c13 = astrofits.Column(name='FREQRES', format='1D', unit='Hz', array=numpy.array([ self.freq[0].chWidth, ] * len(scanList))) # Bandwidth of the system (Hz) c14 = astrofits.Column(name='BANDWID', format='1D', unit='Hz', array=numpy.array([ self.freq[0].totalBW, ] * len(scanList))) # Frequency axis - 1 c15 = astrofits.Column(name='CRPIX1', format='1E', array=numpy.array([ self.refPix, ] * len(scanList))) c16 = astrofits.Column(name='CRVAL1', format='1D', unit='Hz', array=numpy.array([ self.refVal, ] * len(scanList))) c17 = astrofits.Column(name='CDELT1', format='1D', unit='Hz', array=numpy.array([ self.freq[0].chWidth, ] * len(scanList))) c18 = astrofits.Column(name='CRVAL3', format='1D', unit='deg', array=numpy.array([ 0.0, ] * len(scanList))) # Dec. axis - 4 c19 = astrofits.Column(name='CRVAL4', format='1D', unit='deg', array=numpy.array([ 0.0, ] * len(scanList))) ## Scan rate #c20 = astrofits.Column(name='SCANRATE', format='2E', unit='deg/s', #array=numpy.array([[0,0],]*len(scanList))) # # Calibration information (currently not implemented) # ## System temperature *** UNKNOWN *** #c21 = astrofits.Column(name='TSYS', format='2E', unit='K', #array=numpy.array([[self.tSys,self.tSys],]*len(scanList))) ## CALFCTR *** UNKNOWN *** #c22 = astrofits.Column(name='CALFCTR', format='2E', unit='K', #array=numpy.array([[1,1],]*len(scanList))) # Data c23 = astrofits.Column(name='DATA', format='%iE' % (self.nStokes * self.nChan), unit='UNCALIB', array=numpy.array(mList)) # # Data masking table (currently not implemented) # # Flag table #c24 = astrofits.Column(name='FLAGGED', format='%iB' % (self.nStokes*self.nChan), #array=numpy.array([[0,]*self.nStokes*self.nChan for s in scanList])) # # Calibration information (currently not implemented) # ## TCAL *** UNKNOWN *** #c25 = astrofits.Column(name='TCAL', format='2E', unit='Jy', #array=numpy.array([[1,1] for s in scanList])) ## TCALTIME *** UNKNOWN *** #c26 = astrofits.Column(name='TCALTIME', format='16A', #array=numpy.array(['UNKNOWN',]*len(scanList))) # # Pointing information (currently not implemented) # ## Azimuth *** UNKNOWN *** #c27 = astrofits.Column(name='AZIMUTH', format='1E', unit='deg', #array=numpy.array([0,]*len(scanList))) ## Elevation *** UNKNOWN *** #c28 = astrofits.Column(name='ELEVATIO', format='1E', unit='deg', #array=numpy.array([0,]*len(scanList))) ## Parallactic angle *** UNKNOWN *** #c29 = astrofits.Column(name='PARANGLE', format='1E', unit='deg', #array=numpy.array([0,]*len(scanList))) # # Focusing information (currently not implemented and probably never will be) # ## FOCUSAXI *** NOT NEEDED *** #c30 = astrofits.Column(name='FOCUSAXI', format='1E', unit='m', #array=numpy.array([0,]*len(scanList))) ## FOCUSTAN *** NOT NEEDED *** #c31 = astrofits.Column(name='FOCUSTAN', format='1E', unit='m', #array=numpy.array([0,]*len(scanList))) ## FOCUSROT *** NOT NEEDED *** #c32 = astrofits.Column(name='FOCUSROT', format='1E', unit='deg', #array=numpy.array([0,]*len(scanList))) # # Weather information (currently not implemented) # ## Ambient temperature *** UNKNOWN *** #c33 = astrofits.Column(name='TAMBIENT', format='1E', unit='C', #array=numpy.array([0,]*len(scanList))) ## Air pressure *** UNKNOWN *** #c34 = astrofits.Column(name='PRESSURE', format='1E', unit='Pa', #array=numpy.array([0,]*len(scanList))) ## Humidity *** UNKNOWN *** #c35 = astrofits.Column(name='HUMIDITY', format='1E', unit='%', #array=numpy.array([0,]*len(scanList))) ## Wind speed *** UNKNOWN *** #c36 = astrofits.Column(name='WINDSPEE', format='1E', unit='m/s', #array=numpy.array([0,]*len(scanList))) ## Wind direction *** UNKNOWN *** #c37 = astrofits.Column(name='WINDDIRE', format='1E', unit='deg', #array=numpy.array([0,]*len(scanList))) # Gather together all of the needed columns and figure out which ones # store the data and flag tables. This information is needed later to # set the appropriate TDIM keywords. cs = [ c1, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, c18, c19, c23 ] dataIndex = 0 #flagIndex = 0 for i, c in enumerate(cs): try: if c.name == 'DATA': dataIndex = i + 1 #if c.name == 'FLAGGED': #flagIndex = n except NameError: pass colDefs = astrofits.ColDefs(cs) # Create the SINGLE DISH table and update its header sd = astrofits.BinTableHDU.from_columns(colDefs) ## Single disk keywords - order seems to matter sd.header['EXTNAME'] = ('SINGLE DISH', 'SDFITS table name') sd.header['NMATRIX'] = 1 sd.header['OBSERVER'] = (self.observer, 'Observer name(s)') sd.header['PROJID'] = (self.project, 'Project name') sd.header['TELESCOP'] = (self.site.name, 'Telescope name') x, y, z = self.site.geocentric_location sd.header['OBSGEO-X'] = (x, '[m] Antenna ECEF X-coordinate') sd.header['OBSGEO-Y'] = (y, '[m] Antenna ECEF Y-coordinate') sd.header['OBSGEO-Z'] = (z, '[m] Antenna ECEF Z-coordinate') sd.header['SPECSYS'] = ('LSRK', 'Doppler reference frame (transformed)') sd.header['SSYSOBS'] = ('TOPOCENT', 'Doppler reference frame of observation') sd.header['EQUINOX'] = (2000.0, 'Equinox of equatorial coordinates') sd.header['RADESYS'] = ('FK5', 'Equatorial coordinate system frame') ## Data and flag table dimensionality sd.header['TDIM%i' % dataIndex] = ('(%i,%i,1,1)' % (self.nChan, self.nStokes)) #sd.header.set('TDIM%i' % flagIndex, '(%i,%i,1,1)' % (self.nChan, self.nStokes), after='TFORM%i' % flagIndex) ## Data and flag table axis descriptions ### Frequency sd.header['CTYPE1'] = ('FREQ', 'axis 1 is FREQ (frequency)') sd.header['CDELT1'] = self.freq[0].chWidth sd.header['CRPIX1'] = self.refPix sd.header['CRVAL1'] = self.refVal ### Stokes sd.header['CTYPE2'] = ('STOKES', 'axis 2 is STOKES axis (polarization)') if self.stokes[0] < 0: sd.header['CDELT2'] = -1.0 else: sd.header['CDELT2'] = 1.0 sd.header['CRPIX2'] = 1.0 sd.header['CRVAL2'] = float(self.stokes[0]) ### RA sd.header['CTYPE3'] = ('RA', 'axis 3 is RA axis (pointing)') sd.header['CRPIX3'] = 1.0 sd.header['CDELT3'] = -1.0 ### Dec sd.header['CTYPE4'] = ('DEC', 'axis 4 is Dec. axis (pointing)') sd.header['CRPIX4'] = 1.0 sd.header['CDELT4'] = 1.0 self.FITS.append(sd) self.FITS.flush()
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)