Project

General

Profile

Developer's manual

1. Introduction

Signal Chain (SCh) is a JRO ongoing project, which has the aim to develop open source libraries for the signal processing of the information acquired with scientific radars. The final purpose of these libraries is to share them with the scientific community that uses these devices, in order to foster collaboration between the different institutions engaged in this field of study.

2. Installation

  1. Install Python dependencies
    • numpy
    • matplotlib
    • scipy
    • h5py
  2. Download Eclipse: https://eclipse.org/downloads/. The recommended version is "Eclipse IDE for C/C++ Developers".
  3. Install Pydev
  4. Install Subclipse: http://dev.antoinesolutions.com/subclipse.
  5. Open the Subclipse repository perspective
    • Select the Window > Open Perspective > Other… menu option
    • Select the SVN Repository Exploring option
    • Click OK
  6. Add Signal Chain Repository
  7. Download Signal Chain Libraries
    • Open Pydev perspective, analogously as Step 5
    • Select the New > Project... option
    • Select SVN > Checkout Projects from SVN
    • Click Next
    • Select "Use existing repository location"
    • Select the Signal Chain repository
    • Click Next
    • Select http://jro-dev.igp.gob.pe/svn/jro_soft/schain > trunk > schainroot
    • Click Next
    • Select "Check out as a project in the workspace"
    • Click Finish

3. Radar Basics

3.1 Radar principle

The basic principle of a radar can be observed in the animated gif below. The process starts with an electromagnetic pulse generated at the transmitter which is irradiated into the space through the antenna. The pulse travels across the space until it reaches the target. The pulse is intercepted by the target, which absorbs part of the energy and re-irradiates it in all directions. Finally, some of these energy is redirected back to the antenna, where the system receives it and the information is processed to estimate different parameters.

3.2 Transmission Pulse

The distance of the target can be determined with the time it takes the system to receive its echo. This time is how much it takes to the radar signal to travel from the radar to the target and back. Typically, in a radar experiment, multiple pulses are sent with an specific frequency which is called Pulse Repetition Frequency (PRF) and its inverse is called the Inter-Pulse Period (IPP). This period is selected according to the longest expected target's range. If the IPP is too short, a backscattered signal from a long-range target could arrive after the transmission of the next pulse.

3.3 Radar data

After each pulse, the echo is digitally sampled by the radar acquisition system. The samples at a fixed time after the pulse represent the response of the atmosphere at an specific height of the atmosphere . In this manner, this samples can be used to constuct a time series, as can be seen in the next figure. This series is then transformed into the frequency domain to visualize the doppler shifts.

4. Software Description

In Signal Chain, every signal processing algorithm is splitted into separated blocks. This approach can be explained in the image below, where the process of acquiring a digital image is illustrated as a pipeline. The process is splitted into five different blocks. Each one of them plays a different role. The first step is to transform the light into a voltage signal with a sensor array. Next, the analogue signal obtained is amplified with a low-noise amplifier block and, then, it is converted to digital through an ADC. The next block is a DSP, which involves many signal processing algorithms such as filtering or averaging. Finally, the last stage is to store the image obtained in a database.

Analogously, to develop SCh libraries, each algorithm is splitted into a sequence of independent operations such as data reading, interference filtering or results plotting. These agents are deliberately limited in scope and as generic as the algorithm allows. The purpose is to make the modules reusable in different processes and, in this manner, avoid repeating code. This trait is illustrated in the image below.

One of the most common experiments is the MST-ISR-EEJ. In this configuration, three different experiments (MST, ISR and EEJ) are executed simultaneously and their data are stored in the same raw files. MST stands for Mesosphere, Stratosphere, Troposphere and the experiment aims to study the low atmosphere echoes. ISR means Incoherent Scatter Radar and seeks to study the incoherent scatter echoes from the ionosphere. Finally, EEJ stands for Equatorial Electrojet, and the experiment studies the electrical properties of this phenomenum. To execute the experiment, the SCh script starts with the jointed data. The first step is to separate the profiles that belong to each one of the experiments. After this step, the image shows that each experiment runs independently. The MST experiment procceeds to decode the data and do a couple of incoherent integrations. Then, the data is converted to the frequency domain through the FFT. Finally, the data is integrated incoherently and it is plotted to show the results to the user. ISR does exactly the same operations; thus, they use the same operation libraries. The only difference is the parameters that each one of this operations have. Consequently, SCh methodology allows the user to process these two experiments with the same libraries, with only small variations in the input parameters.

4.1 Configuration layers

Every SCh experiment is configured in four basic steps or layers, which are described below.

  1. User Interface.- The configuration can be done with the graphic interface (GUI) or a python script, which is the recommended option for a developer.

  2. Project.- Next, a Project object should be created, which will have an ID, name and a brief description of the experiment being processed.

  3. Units.- Inside the Project, the user will create Units, which will contain the different operations or tasks that SCh will performed to the data. There are three main types of Units:

    • Reading Unit, this units read the data that is acquired by the radar system. This data can be voltages or spectral information.
    • Writing Unit, once the data has been processed, the information obtained or the modified data can be stored in files that can be read later.
    • Processing Unit, they contain the different mathematical operations or algorithms that will be performed to the data of an specific type. The type of operations that can be selected depend on the type of Processing Unit that it is being used. There are four main types of Processing Units: Voltage, Spectra, Correlations and Parameters.
  4. Operations.- Finally, the user should add to the Units all the algorithms or mathematical operations. There are two type of operations:

    • Processing Operation, algorithms or calculations to obtain specific information from the data, such as calculation of moments, winds estimation or decoding.
    • Graphic Operation, plots to visualize the results of the signal processing algorithms

4.2 Basic structure

The basic pipeline of a SCh can be seen in the image below. The first module to be defined is the Reading module, which means that the first step is to define the data that the software will read and use as input. Next, a Processing Unit is defined to include all the operations that will be performed to the signal. Finally, a Writing Unit is added to output the results of the signal processing chain and to make further analysis.

4.3 Files distribution

Finally, to develop new libraries, it is necessary to be familiarized with the files distribution of Signal Chain. If the repository location was added correctly to the workspace, something as the next figure should appear. The files that will be used are all inside schainroot>source>schainpy. There are two main directores:

  • model, contains the code of the signal processing libraries developed for the radar experiments and it is divided according to the type of data being analyzed.
  • scripts, contains the python scripts that are executed to start the signal processing algorithms.

Likewise, the model directory is divided in different directories that organize the different libraries developed into four main categories:

  • data, libraries that model the data passed through the different units and operations. There are four main types of data: voltage, spectra, correlation and parameters. Each one of them have different atrributes and methods according to their characteristics.
  • graphics, graphic libraries to visualize the information obtain from the data in realtime. In the same way, this libraries are organized in different python scripts according to the type of data they plot.
  • io, contains the reading and writing units code.
  • proc, libraries that contain all the processing units and operations programming code.

5. Development examples

In this section, some examples to configure basic experiments will be addressed. The first one is a basic example to help understand the experiment configuration of Signal Chain. Then, the manual details how to create new operations and, finally, the user will understand how to develop his own reading and processing units.

5.1 Script Example

In this part, a simple example will be developed to understand the experiment configuration through a python script. In the figure below, the program structure can be seen. The script will start with a Reading Unit; in this case, Voltage data reader. Then, it will continue with a Voltage Processing Unit, which will contain two operations, a Decoder and Coherent Integrations. Next, a Spectra Processing Unit is added. Inside this Processing Unit, two more operations are added. One is Incoherent Integrations while the other one is a RTI Plot, to visualize the data being processed. Finally, a Writing Unit is created to store the processed data.

The first step is to set the search path for modules. The path for python libraries such as numpy, matplotlib is already set by default, so they only need to be imported. However, to use the Signal Chain modules as well, the workspace path needs to be added, which is done with the lines of code below.

import os, sys

path = os.path.split(os.getcwd())[0]
path = os.path.split(path)[0]

sys.path.insert(0, path)

Next, some necessary variables are defined, such the XML file name, the path with the input files, and the path were the output files are going to be saves.

filename = "school_test.xml"
path ="../../../data/rawdata/"
pathfile = os.environ['HOME']

The Project object mentioned in 4.1 is created. The object's name is controllerObj and needs to have an id, name, and a brief description to identify it.

from schainpy.controller import Project

controllerObj = Project()
controllerObj.setup(id = '101', name='test01', description='Basic experiment')

Now, we proceed to configure the Reading Unit. In this case, the unit will read Voltage data so we need to specify the datatype as 'VoltageReader'. In this part, it is necessary to define the 'path' variable, which is the directory where the files processed are located.

readUnitConfObj = controllerObj.addReadUnit(datatype='VoltageReader',
                                            path=path,
                                            startDate='2014/01/31',
                                            endDate='2014/03/31',
                                            startTime='00:00:00',
                                            endTime='23:59:59',
                                            online=0,
                                            delay=5,
                                            walk=0)

The Voltage Processing Unit is created. As can be seen below, the input of this unit is the Reading Unit configured above. Next, the operations are added to this Processing Unit using the method 'addOperation'. The Decoder and CohInt operations are created. The CohInt operation integrates the data coherently, i.e. it averages the incoming data in order to increase the Signal to Noise Ratio (SNR). Consequently, the number of averages needs to be defined. This is done using the method 'addParameter'.

procUnitConfObj0 = controllerObj.addProcUnit(datatype='VoltageProc', inputId=readUnitConfObj.getId())

opObj01 = procUnitConfObj0.addOperation(name='Decoder', optype='other')

opObj02 = procUnitConfObj0.addOperation(name='CohInt', optype='other')
opObj02.addParameter(name='n', value='5', format='int')

The Spectra Processing Unit is created, and the input is the Voltage Processing Unit defined before. Signal Chain makes the conversion from time to frequency domain automatically. It just needs the user to specify the number of FFT points to be calculated for the spectrum, which is the same number of profiles to be stacked before converting to the frequency domain. Then, the Incoherent Integration operation is added and the number of integrations 'n' is specified as a parameter. To visualize the data, a RTI Plot is added.

procUnitConfObj1 = controllerObj.addProcUnit(datatype='SpectraProc', inputId=procUnitConfObj0.getId())
procUnitConfObj1.addParameter(name='nFFTPoints', value='16', format='int')

opObj11 = procUnitConfObj1.addOperation(name='IncohInt', optype='other')
opObj11.addParameter(name='n', value='2', format='int')

opObj12 = procUnitConfObj1.addOperation(name='SpectraPlot', optype='other')
opObj12.addParameter(name='id', value='1', format='int')

The spectra writer is introduced to store the processed the data. Two parameters are added to the operation. The first one is the path where the files will be stored, while the second one is the number of blocks or spectra that will be stored per file.

opObj13 = procUnitConfObj1.addOperation(name='SpectraWriter', optype='other')
opObj13.addParameter(name='path', value=path)
opObj13.addParameter(name='blocksPerFile', value='100', format='int')

After finishing the structure of the program, a XML file is created. This same XML file is produced when the GUIDE is being used.

print "Escribiendo el archivo XML"
controllerObj.writeXml(filename)
print "Leyendo el archivo XML"
controllerObj.readXml(filename)

Finally, this is the code that Signal Chain actually executes. The Project, Processing Units and Operation objects are created, connected and the script is executed.

controllerObj.createObjects()
controllerObj.connectObjects()
controllerObj.run()

To run the program, write it in a .py file, and move the file into the folder: schainroot > source > schainpy > scripts. If everything works fine, a window showing the Spectral Plot should open.

5.2 Operation Example

In this section, an Operation Unit will be created, more specifically, a simpler version of the Coherent Integration operation that is already in the SCh libraries. This exercise will help to understand how to develop SCh libraries and how the software handles the data.

We will start with the script developed in section 5.1 and replace the Coherente Integration block. For this, write .py with the script

First, go to schainroot>source>schainpy>model>proc

class MyOperation(Operation):

    nProfiles = 0

    buffer = None
    def run(self, dataOut, n):
        #Doesnt fo any further
        dataOut.flagNoData = True
        #Copy the data
        data = dataOut.data
        #Set buffer
        if self.buffer == None:
            self.buffer = data/n
        else:
            self.buffer += data/n

        self.nProfiles += 1
        #Check if enough integrations
        if self.nProfiles == n:
            dataOut.data = self.buffer
            dataOut.flagNoData = False
            self.buffer = None
            self.nProfiles = 0

5.3 Reading Unit Example

class MyReader(ProcessingUnit):

    path = None
    fileList = None
    fileIndex = 0
    nBlocks = 0
    blockIndex = 0

    #Metadata
    timeZone = None
    paramInterval = None
    heightList = None

    #Data
    data = None
    utctime = None
    def __init__(self):
        #Initialize dataOut as a type of data
        self.dataOut = Parameters()
        return
    def run(self, **kwargs):

        #Initial setup
        if not(self.isConfig):
            self.path = kwargs['path']
            self.__setup()
        #Have we read all blocks?
        if self.blockIndex == self.nBlocks: 
            #Have we read all files?  
            if self.fileIndex == len(self.fileList):
                #if yes, the program is terminated 
                self.dataOut.flagNoData = True
                return 0                    
            else:
                #if not, we read next file
                self.__readNextFile()
        #Read next block
        self.__readNextBlock()

        return

Functions

    def __setup(self):
        #Search available files
        fileList = glob.glob1(self.path, "*%s" %".hdf5")
        fileList.sort()
        #Save configuration
        self.fileList = fileList    
        self.isConfig = True
    def __readNextFile(self):

        #Read file
        filename = os.path.join(self.path,self.fileList[self.fileIndex])
        fp = h5py.File(filename,'r')

        #Read metadata files
        grp1 = fp['Metadata']
        self.timeZone = grp1['timeZone'].value
        self.paramInterval = grp1['paramInterval'].value
        self.heightList = grp1['heightList'].value

        #Read data files
        grp2 = fp['Data']
        self.utctime = numpy.squeeze(grp2['utctime'].value)

        grp22 = grp2['data_param']
        ds0 = grp22['table0'].value
        ds1 = grp22['table1'].value
        ds2 = grp22['table2'].value

        #Rearranging them for dataOut object
        self.data = numpy.zeros((ds0.shape[0], 3, ds0.shape[1], ds0.shape[2]))
        self.data[:,0,:,:] = ds0
        self.data[:,1,:,:] = ds1
        self.data[:,2,:,:] = ds2

        #Setting variables
        self.nBlocks = ds0.shape[2]
        self.blockIndex = 0
        self.fileIndex += 1

        return
    def __readNextBlock(self):
        #Metadata
        self.dataOut.timeZone = self.timeZone
        self.dataOut.paramInterval = self.paramInterval
        self.dataOut.heightList = self.heightList
        self.dataOut.channelList = [0,1]

        #Data
        self.dataOut.utctimeInit = self.utctime[self.blockIndex]
        self.dataOut.utctime = self.utctime[self.blockIndex]
        self.dataOut.data_param = self.data[:,:,:,self.blockIndex]
        self.dataOut.flagNoData = False
        self.blockIndex += 1
        return

Final script

import os, sys

path = os.path.split(os.getcwd())[0]
path = os.path.split(path)[0]

sys.path.insert(0, path)

filename = "school_test2.xml"
figpath = os.path.join(os.environ['HOME'],'Pictures')
path = os.path.join(os.environ['HOME'],'Pictures')
from schainpy.controller import Project
controllerObj = Project()
controllerObj.setup(id = '102', name='test02', description='Reader/Writer experiment')
readUnitConfObj = controllerObj.addReadUnit(datatype='MyReader',
                                            path=path,
                                            startTime = '00:00:00',
                                            endTime = '23:59:59',
                                            startDate = '2000/01/31',
                                            endDate = '2012/01/31')
procUnitConfObj2 = controllerObj.addProcUnit(datatype='ParametersProc', inputId=readUnitConfObj.getId())

opObj21 = procUnitConfObj2.addOperation(name='ParametersPlot', optype='other')
opObj21.addParameter(name='id', value='5', format='int')
opObj21.addParameter(name='wintitle', value='Radial Velocity Plot', format='str')
opObj21.addParameter(name='save', value='1', format='bool')
opObj21.addParameter(name='figpath', value=figpath, format='str')
opObj21.addParameter(name='xmin', value='0', format='float')
opObj21.addParameter(name='xmax', value='0.5', format='float')
opObj21.addParameter(name='zmin', value='-0.25', format='float')
opObj21.addParameter(name='zmax', value='0.25', format='float')
opObj21.addParameter(name='paramIndex', value='1', format='int')
opObj21.addParameter(name='colormap', value='0', format='bool')
print "Escribiendo el archivo XML"
controllerObj.writeXml(filename)
print "Leyendo el archivo XML"
controllerObj.readXml(filename)

controllerObj.createObjects()
controllerObj.connectObjects()
controllerObj.run()

5.4 Plotting Example

class MyPlot(Figure):

    def __init__(self):
        return
    def getSubplots(self):
        ncol = 2
        nrow = 1

        return nrow, ncol
    def run(self, dataOut, id, show = True, wintitle = None, figpath = None, 
            xmin = None, xmax = None, ymin = None, ymax = None, zmin = None, zmax = None, 
            xlabel = None, ylabel = None, title = None):
        #Read data from dataOut object
        x = dataOut.getFreqRange(1)
        y = dataOut.heightList
        zdB = numpy.log10(dataOut.data_spc)
        thisDatetime = datetime.datetime.utcfromtimestamp(dataOut.getTimeRange()[0])
        #Is it configured?
        if not(self.isConfig):
            #Create Figure
            self.createFigure(id = id,
                  wintitle = "Plot Example",
                  widthplot = 250,
                  heightplot = 250,
                  show=show)

            #Create Axes and adds them to axesList attribute
            #nrow, ncol, row, col, colspan, rowspan
            self.addAxes(1,2,0,0,1,1)
            self.addAxes(1,2,0,1,1,1)

            #Plot limits
            xmin = x[0]
            xmax = x[-1]
            ymin = y[0]
            ymax = y[-1]
            zmin = numpy.nanmin(zdB)
            zmax = numpy.nanmax(zdB)

            #Labels
            xlabel = "Frequency"
            ylabel = "Heights"
            title = "My Plot" 

            #Configure flag
            self.iConfig = True
        for i in range(2):
            title = "Channel %d" %(i)
            #Plot data
            axes = self.axesList[i]
            axes.pcolor(x, y, zdB[i,:,:],
                        xmin=xmin, xmax=xmax, ymin=ymin, ymax=ymax, zmin=zmin, zmax=zmax,
                        xlabel=xlabel, ylabel=ylabel, title=title,
                        ticksize=9, cblabel='')
        #Show Figure          
        self.draw()
        #Name of the File
        name = thisDatetime.strftime("%Y%m%d_%H%M%S")
        figfile = self.getFilename(name)

        #Save Figure
        self.save(figpath=figpath,
                  figfile=figfile,
                  save=True,
                  ftp="",
                  wr_period="",
                  thisDatetime=thisDatetime)

Script

import os, sys

path = os.path.split(os.getcwd())[0]
path = os.path.split(path)[0]

sys.path.insert(0, path)

path ="../../../data/pdata/"
filename = "school_test3.xml"
figpath = os.path.join(os.environ['HOME'],'Pictures/school/')

#-----------------------------------------------------------------------------------------------------------------
from schainpy.controller import Project
controllerObj = Project()
controllerObj.setup(id = '103', name='test03', description='Plot experiment')
readUnitConfObj = controllerObj.addReadUnit(datatype='Spectra',
                                            path=path,
                                            startDate='2010/12/18',
                                            endDate='2015/12/22',
                                            startTime='00:00:00',
                                            endTime='23:59:59',
                                            online=0,
                                            walk=0,
                                            expLabel='')

procUnitConfObj1 = controllerObj.addProcUnit(datatype='Spectra', inputId=readUnitConfObj.getId())
opObj11 = procUnitConfObj1.addOperation(name='MyPlot', optype='other')
opObj11.addParameter(name='id', value='10', format='int')
opObj11.addParameter(name='figpath', value=figpath)
print "Escribiendo el archivo XML"
controllerObj.writeXml(filename)
print "Leyendo el archivo XML"
controllerObj.readXml(filename)

controllerObj.createObjects()
controllerObj.connectObjects()
controllerObj.run()

6. Related Links

Radarops.gif View (15.5 KB) Julio Oscanoa, 12/22/2015 09:48 AM

pulsradar.jpg View (11.3 KB) Julio Oscanoa, 12/22/2015 09:53 AM

datosRadar.png View (105 KB) Julio Oscanoa, 12/22/2015 10:00 AM

camera.jpg View (28.8 KB) Julio Oscanoa, 12/22/2015 10:04 AM

FoldersFigure.png View (132 KB) Julio Oscanoa, 12/22/2015 12:03 PM

ModelsFigure.png View (258 KB) Julio Oscanoa, 12/22/2015 12:03 PM

layers.png View (1.04 MB) Julio Oscanoa, 12/22/2015 02:01 PM

BasicStructure.png View (307 KB) Julio Oscanoa, 01/07/2016 10:01 AM

MST_ISR_EEJ.png View (43.4 KB) Julio Oscanoa, 01/07/2016 03:55 PM

flagNoData.jpg View (28.1 KB) Julio Oscanoa, 08/31/2016 08:43 PM

MyOperation.jpg View (27.7 KB) Julio Oscanoa, 08/31/2016 08:43 PM

BasicExperiment.jpg View (29.1 KB) Julio Oscanoa, 08/31/2016 08:43 PM

wuDiagram.png View (67.1 KB) Julio Oscanoa, 09/08/2016 05:26 PM