#!/usr/bin/env python
# coding: utf-8
# # MUSIC (MUltiple SIgnal Classification) Notebook - SIMULATION
# ***This notebook is essentially a copy of the MUSIC.ipynb, except that it shows how to insert artificial MSTID's into the data processing stream for the purpose of evaluating the processing algorithms.***
#
# Click here to jump to the part where the data simulator is used.
# [simulate]: The MUSIC (MUltiple SIgnal Classification) algorithm is a rather generic and well-know algorithm in signal process used for the detection of multiple waves within an array of sensors [Schmidt, 1986]. MUSIC was originally used in SuperDARN by Samson et al. [1990] and Bristow et al. [1994] for the estimation of the characteristics of Medium Scale Traveling Ionospheric Disturbances (MSTIDs) detected by SuperDARN Radars. These papers have inspired this implementation of the MUSIC algorithm.
#
# While this code has been written with SuperDARN detected MSTIDs in mind, it may be useful for working with other wave-like perturbations moving across the field of view of some geophysical instrument.
#
# This notebook demonstrates the use of the DaViTPy MUSIC module for MSTID parameter estimation.
#
# The DaViTPy MUSIC module is used by creating a musicArray object, which includes built-in methods for keeping track of all steps in the processing algorithm. As the data is processed, all preceeding variants of the data are saved within the object and a history is created. This makes it very easy to see what has been done with the data at any step along the way.
#
# *Written by N.A. Frissell, 4 November 2013.*
# **References**
#
# Bristow, W. A., R. A. Greenwald, and J. C. Samson (1994), Identification of high-latitude acoustic gravity wave sources using the Goose Bay HF Radar, J. Geophys. Res., 99(A1), 319–331, doi:10.1029/93JA01470.
#
# Samson, J. C., R. A. Greenwald, J. M. Ruohoniemi, A. Frey, and K. B. Baker (1990), Goose Bay radar observations of Earth-reflected, atmospheric gravity waves in the high-latitude ionosphere, J. Geophys. Res., 95(A6), 7693–7709, doi:10.1029/JA095iA06p07693.
#
# Schmidt, R.O., "Multiple emitter location and signal parameter estimation," Antennas and Propagation, IEEE Transactions on, vol.34, no.3, pp.276,280, Mar 1986, doi:10.1109/TAP.1986.1143830.
# In[1]:
#Import the modules we need.
get_ipython().run_line_magic('pylab', 'inline')
import datetime
from matplotlib import pyplot as plt
import numpy as np
from davitpy import pydarn
# # MUSIC Processing Example
# ## Loading Data and Basic Plotting
# In[2]:
#Choose the radar and time of the data that we want to look at.
#Then, connect to the data source using the standard DaViTPy
#pydarn.sdio.radDataOpen() routine.
rad='wal'
sTime = datetime.datetime(2011,5,9,8)
eTime = datetime.datetime(2011,5,9,19)
myPtr = pydarn.sdio.radDataOpen(sTime,rad,eTime=eTime)
# In[3]:
# Now create the data object.
# By creating this object, data is taken from the SuperDARN database and rearranged into a numpy.array with
# the shape (Nr Times, Nr Beams, Nr Gates). Missing data is stored as np.nan.
# By default, this command only loads in data where the ground scatter flag is True.
# The fovModel='GS' indicates to calculate the field-of-view coordinates in using the ground-scatter mapping
# formula. This is standard when looking at MSTIDs using ground scatter. See Bristow et al. [1994] for details.
dataObj = pydarn.proc.music.musicArray(myPtr,fovModel='GS')
# In[4]:
# We can list all of the data sets stored in this object. Right now there is only one data set, but
# as we go along, each step of processing will create a new data set.
# Each data set is simply stored as an attribute of the dataObj, but the names of all of the data sets can be
# accessed through the get_data_sets() method.
dataObj.get_data_sets()
# In[5]:
# Information about the data set is saved in it's metadata dictionary. Some of the information in the metadata
# dictionary is used to control certain plotting and processing variables in this module.
dataObj.DS000_originalFit.printMetadata()
# In[6]:
# The music module also keeps track of each step of processing in using the data set's history attribute.
dataObj.DS000_originalFit.printHistory()
# In[7]:
# The 'active' attribute of dataObj is a reference to the most recently used data set of dataObj. It is also
# the default data set used by all DaViTPy MUSIC processing and plotting routines. You can see this command
# produces the same result as the cell above.
dataObj.active.printHistory()
# In[8]:
# We can create an RTI plot and a fan plot from the data we loaded.
# You can see that both range gate and geographic latitude are given on the y-axis, and the solar terminator
# is also shaded in. The terminator does not go below approximately range gate 10. This is because the ground
# scatter mapping formula is not defined for close-in range gates.
fig = pydarn.plotting.musicRTI(dataObj)
# In[9]:
# We can also make a fan plot.
plotTime = datetime.datetime(2011,5,9,14)
fig = pydarn.plotting.musicFan(dataObj,time=plotTime)
# ## Data Selection
# In[10]:
# We want to focus on just the data that contains the MSTID we are looking for, so we can apply some limits.
# proc.music.defineLimits() does not actually modify the data, it just marks the data by putting an an entry
# into the dataObj.active.metadata dictionary. The limits will be applied later. Right now, you can see
# what data will be eliminated by making a new RTI plot.
pydarn.proc.music.defineLimits(dataObj,gateLimits=[30,45])
fig = pydarn.plotting.musicRTI(dataObj)
# In[11]:
# We also to restrict the amount of time we process. Before actually running the music algorithm, we
# will be filtering the data using a FIR filter that will eat up data at the beginning and end of the filter.
# We can calculate exactly how much time that will be if we know some of the filter characteristics and
# the time resolution of the data. This can then be used to give us new start and end times.
# For now, let's say we are going to use a filter with 101 taps and a dataset with 120 s resolution.
numtaps = 101
timeres = 120
#Let's also say that we are interested in the MSTID feature between 1400 and 1600 UT.
sTime_of_interest = datetime.datetime(2011,5,9,14)
eTime_of_interest = datetime.datetime(2011,5,9,16)
#Now calculate the new start and end times...
new_times = pydarn.proc.music.filterTimes(sTime_of_interest, eTime_of_interest, timeres, numtaps)
pydarn.proc.music.defineLimits(dataObj,timeLimits=new_times)
fig = pydarn.plotting.musicRTI(dataObj)
# In[12]:
# Now we apply the limits and replot once more.
# Note that many of the processing routines will automatically call applyLimits() before they
# run the processing algorithm.
dataObj.active.applyLimits()
fig = pydarn.plotting.musicRTI(dataObj)
# In[13]:
# Note that a new data set was created when we applied the limits.
dataObj.get_data_sets()
# In[14]:
# Also note that the history of the new object was updated.
dataObj.active.printHistory()
# ## Data Processing
# ### Interpolation and Cartesian Coordinates
# In[15]:
# Before we feed into the MUSIC Algorithm, we don't want our data to have any gaps in time or space.
# Let's start by interpolating in space along the beams.
pydarn.proc.music.beamInterpolation(dataObj)
# In[16]:
fig = plt.figure(figsize=(20,10))
ax = fig.add_subplot(121)
pydarn.plotting.musicPlot.musicFan(dataObj,plotZeros=True,dataSet='originalFit',axis=ax,time=plotTime)
ax = fig.add_subplot(122)
pydarn.plotting.musicPlot.musicFan(dataObj,plotZeros=True,axis=ax,time=plotTime)
# In[17]:
# We also want to interpolate in time. timeres=120 [seconds] was set in an earlier cell.
pydarn.proc.music.timeInterpolation(dataObj,timeRes=timeres)
# In[18]:
pydarn.plotting.musicPlot.timeSeriesMultiPlot(dataObj,dataSet='timeInterpolated',dataSet2='beamInterpolated')
# In[19]:
# We also want need to calculate a local cartesian grid for each cell we are going to use.
pydarn.proc.music.determineRelativePosition(dataObj)
# In[20]:
#The black cell marks the center of the array.
fig = pydarn.plotting.plotRelativeRanges(dataObj,time=plotTime)
# ### Data Simulation
# In[21]:
# At this point, we can inject a simulated MSTID into the processing chain. This way, we can see exactly what
# processing is doing to ideal data. It is really easy to inject a default simulated wave.
pydarn.proc.music.simulator(dataObj)
fig = pydarn.plotting.musicRTI(dataObj)
fig = pydarn.plotting.musicFan(dataObj,time=plotTime)
# In[22]:
#Or, you can do something more complicated. Here we define two custom MSTIDs and add some noise.
# Each MSTID is evaluated as a cosine and then summed together. The cosine evaluated is::
# sig = amp * np.cos(kx*xgrid + ky*ygrid - 2.*np.pi*f*t + phi) + dc
sigs = []
# (amp, kx, ky, f, phi, dcOffset)
sigs.append(( 5, -0.01, 0.010, 0.0007, 0, 5.))
sigs.append(( 10, 0.022, -0.023, 0.0004, 0, 5.))
#And some arbitrary white gaussian noise...
noiseFactor = 4
# In[23]:
pydarn.proc.music.simulator(dataObj,sigs=sigs,noiseFactor=noiseFactor)
fig = pydarn.plotting.musicRTI(dataObj)
fig = pydarn.plotting.musicFan(dataObj,time=plotTime)
# ### Filtering
# In[24]:
# Now filter the data.
# numtaps=101 was set in an above cell. The cutoff_frequencies are in Hz.
filt = pydarn.proc.music.filter(dataObj, numtaps=numtaps, cutoff_low=0.0003, cutoff_high=0.0012)
# In[25]:
# At this point, the data has been filtered and saved to a new data set in dataObj.
# Before we look at the data, let's look at the transfer function and impulse response of the data.
fig = filt.plotTransferFunction(xmax=0.004)
fig = filt.plotImpulseResponse()
# In[26]:
# Let's look at the filtered RTI plot. We should set autoScale to True since the magnitudes will be much
# lower than the original data.
fig = pydarn.plotting.musicRTI(dataObj,autoScale=True)
# In[27]:
# You can see that the filter already marked off the data that you shouldn't use.
# Just applyLimits() and replot.
dataObj.active.applyLimits()
fig = pydarn.plotting.musicRTI(dataObj,autoScale=True)
# In[28]:
# Look at a fan plot.
fig = pydarn.plotting.musicFan(dataObj,time=plotTime,autoScale=True)
# ### Spectral Analysis
# In[29]:
# We have now run all of the processing needed to feed the data into the spectral analysis and MUSIC.
# Let's print the history just to recap what we have done.
dataObj.active.printHistory()
# In[30]:
# First thing to do is to calculate the FFT of every cell...
pydarn.proc.music.calculateFFT(dataObj)
# In[31]:
# We can look at the spectrum of select cells...
pydarn.plotting.spectrumMultiPlot(dataObj,xlim=(-0.0025,0.0025))
pydarn.plotting.musicPlot.spectrumMultiPlot(dataObj,plotType='magnitude',xlim=(0,0.0025))
pydarn.plotting.musicPlot.spectrumMultiPlot(dataObj,plotType='phase',xlim=(0,0.0025))
# In[32]:
# We can also plot the full spectrum. Here, every FFT bin contains 16 slices,
# showing the data for each of the 16 radar beams from left to right.
# Range gates are shown on the y-axis.
pydarn.plotting.plotFullSpectrum(dataObj,xlim=(0,0.00175))
# ### MUSIC Processing
# In[33]:
# Calculate the cross-spectral matrix Dlm.
pydarn.proc.music.calculateDlm(dataObj)
# In[34]:
# We can look at the Dlm matrix. This usually isn't necessary for routine processing, but
# it is good to know where the numbers are going...
pydarn.plotting.plotDlm(dataObj)
# In[35]:
# Now, we finally run detect the horizontal wave numbers.
pydarn.proc.music.calculateKarr(dataObj)
# In[36]:
# We can then plot the final result.
pydarn.plotting.musicPlot.plotKarr(dataObj)
# In[37]:
# Again, we can see all of the history gone into processing this plot.
dataObj.active.printHistory()
# ### Feature/Signal Detection
# In[38]:
# We can use the image processing library in scikit-image to automatically find the peaks representing signals.
pydarn.proc.music.detectSignals(dataObj)
pydarn.plotting.musicPlot.plotKarr(dataObj)
# In[39]:
#All of the parameters connected with these signals is located in the sigDectect attribute.
dataObj.active.sigDetect.info
# In[40]:
# To get some insight on how the signal processing algorithm works, we can plot the detected groups.
# You can adjust the threshold and neighborhood keywords on pydarn.proc.music.detectSignals() to tweak
# the autodetection.
pydarn.plotting.plotKarrDetected(dataObj)
# In[ ]: