radarkit

A small placeholder package


License
MIT
Install
pip install radarkit==0.0.1

Documentation

build status

RadarKit

First of all. Thanks for your interest in the framework! 😄 👍 👊

The RadarKit is a straight C framework. This is a toolkit with various components of a radar signal processor. Mainly the real-time operations of data collection, data transportation through network, rudimentary processing from raw I/Q data to base moment products. The main idea is to have user only implement the interface between a digital transceiver, a pedestal, and a generic health relay. RadarKit combines all of these information, generates radar product files, provides display live streams and redirects the control commands to the hardware.

System Requirements

  • Processors capable of SSE, SSE2, SSE3
  • Optional: AVX, AVX-256

Getting the Project

Follow these steps to get the project

  1. Clone a git project using the following command in Terminal:

    git clone https://git.arrc.ou.edu/cheo4524/radarkit.git
  2. Get the required packages, which can either be installed through one of the package managers or compiled from source.

    Debian
    apt-get install libfftw3-dev libnetcdf-dev
    CentOS 7
    yum install epel-release
    yum install fftw-devel netcdf-devel
    Mac OS X

    I use Homebrew as my package manager for macOS. I highly recommend it.

    brew install fftw homebrew/science/netcdf
  3. Compile and install the framework.

    make
    sudo make install

Basic Usage for a Radar Host

  1. Initialize a radar object (although RadarKit is not an objective implementation but it is easier to think this way). Supply the necessary tranceiver routines and pedestal routines. The health relay is omitted here for simplicity.

    #include <RadarKit.h>
    
    int main() {
        RKRadar *radar = RKInit();
        RKSetTransceiver(radar, NULL, transceiverInit, NULL, NULL);
        RKSetPedestal(radar, NULL, pedestalInit, NULL, NULL);
        RKGoLive(radar);
        RKWaitWhileActive(radar);
        RKFree(radar);
    }
  2. Set up a transceiver initialization and run-loop routines. The initialization routine returns a user-defined pointer, and a run-loop routine receives I/Q data. The initialization routine must return immediately, and the run-loop routine should be created as a separate thread.

    RKTransceiver transceiverInit(RKRadar *radar, void *userInput) {
        // Allocate your own resources, define your structure somewhere else
        UserTransceiverStruct *resource = (UserTransceiverStruct *)malloc(sizeof(UserTransceiverStruct));
        
        // Be sure to save a reference to radar
        resource->radar = radar
        
        // Create your run loop as a separate thread so you can return immediately
        pthread_create(&resource->tid, NULL, transceiverRunLoop, resource);
        
        return (RKTransceiver)resource;
    }
    
    void *transceiverRunLoop(void *in) {
        // Type cast the input to something you defined earlier
        UserTransceiverStruct *resource = (UserTransceiverStruct *)in;
        
        // Now you can recover the radar reference you provided in init routine.
        RKRadar *radar = resource->radar;
        
        // Here is the busy run loop
        while (radar->active) {
            RKPulse *pulse = RKGetVacantPulse(radar);
            pulse->header.gateCount = 1000;
            
            // Go through both polarizations
            for (int p = 0; p < 2; p++) {
                // Get a data pointer to the 16-bit data
                RKInt16C *X = RKGetInt16CDataFromPulse(pulse, p);
                // Go through all range gates and fill in the samples
                for (int g = 0; g < 1000; g++) {
                    // Copy the I/Q samples from hardware interface
                    X->i = 0;
                    X->q = 1;
                    X++;
                }
            }
            RKSetPulseHasData(radar, pulse);
        }
    }
  3. Set up a set of pedestal initialization and run-loop routines. The initialization routine returns a user-defined pointer, and a run-loop routine receives position data. The initialization routine must return immediately, and the run-loop routine should be created as a separate thread.

    RKPedestal pedestalInit(RKRadar *radar, void *userInput) {
        // Allocate your own resources, define your structure somewhere else
        UserPedestalStruct *resource = (UserPedestalStruct *)malloc(sizeof(UserPedestalStruct));
        
        // Be sure to save a reference to radar
        resource->radar = radar
        
        // Create your run loop as a separate thread so you can return immediately
        pthread_create(&resource->tid, NULL, pedestalRunLoop, resource);
        
        return (RKPedestal)resource;
    }
    
    int pedestalRunLoop(void *in) {
        // Type cast the input to something you defined earlier
        UserPedestalStruct *resource = (UserPedestalStruct *)in;
        
        // Now you can recover the radar reference you provided in init routine.
        RKRadar *radar = resource->radar;
        
        // Here is the busy run loop
        while (radar->active) {
            RKPosition *position = RKGetVacantPosition(radar);
            
            // Copy the position from hardware interface
            position->az = 1.0;
            position->el = 0.5;
            RKSetPositionReady(radar, position);
        }
    }
  4. Set up health relay initialization and run-loop routines just like the previous two examples.

  5. Build the program and link to the RadarKit framework. Note that the required packages should be applied too.

    gcc -o program program.c -lRadarKit -lfftw -lnetcdf

This example is extremely simple. Many optional arguments were set to NULL (execution and free routines were omitted). The actual radar will be more complex but this short example illustrates the simplicity of using RadarKit to abstract all the DSP and non-hardware related tasks.

Basic Usage on Signal Processing Space

A seprate processing space to generate high-level products is implemented in Python.

Design Philosophy

Three major hardware components of a radar: (i) a digital transceiver, (ii) a pedestal, and (iii) a health relay (auxiliary controller) are not tightly coupled with the RadarKit framework. Only a set of protocol functions are defined so that the RadarKit can be interfaced with other libraries, which are specific to the hardware and/or vendor design. It is the responsibility of user to implement the appropriate interface routines to bridge the data transport and/or control commands. There are three functions needed for each hardware: init, exec and free, which are routines to allocate an object--akin to object oriented programming, althought RadarKit is a straight C implementation, interact with the object and deallocate the object, respectively. The exec routine has the form to accept text command and produce text response. Some keywords for the command are already defined in the framework so user should not use them. They are intercepted prior to passing down to the exec routine. Detailed usage on these functions will be discussed in detail later.

The digital transceiver is the hardware that requires high-speed data throughput. RadarKit is designed so that redudant memory copy is minimized. That is, a pointer to the memory space for payload will be provided upon a request. User defined routines fill in the data, typically through a copy mechanism through DMA to transport the I/Q data from a transceiver memory to the host memory, which is initialized and managed by RadarKit. The fundamental form is signed 16-bit I and Q, which is a part of RKPulse defined in the framework.

The pedestal is the hardware that is usually low speed, typically on the orders of 10 KBps if the position reading is provided at about 100 samples per second. A RadarKit position structure RKPosition is defined in the framework. If an interface software pedzy is used, which is a light weight pedestal controller, RadarKit can readily ingest position data through a network connection. Otherwise, an RKPedestalPedzy replacement can be implemented to provide same functionality. In this case, user is also free to define a new position type. The RadarKit framework does not restrict this definition.

The health relay is the hardware that is also low speed, typically on the orders of 1 KBps. This is also the hardware that can be called an auxiliary controller, where everything else is interfaced through this relay and the health information is probed through this controller. A RadarKit health structure RKHealth is defined in the framework. More than one health node can be implemented. They provide health information using JSON strings through TCP/IP socket connections. If an interface software tweeta or tweeto is used, RadarKit can readily ingest auxiliary hardware health data through a TCP/IP network connection. Otherwise, an RKHealthRelayTweeta replacement can be implemented to provide same functionality. The RadarKit framework does not restrict this definition.

Base radar products are generated on a ray-by-ray basis. Each ray is of type RKRay. Once a sweep is complete, a Level-II data file in NetCDF format will be generated. Live streams and can be view through a desktop application iRadar.

Radar Struct

This is about the only structure you need to worry about. A radar structure represents an object-like structure where everything is encapsulated.

Life Cycle

These are functions that allocate and deallocate a radar struct.

RKRadar *RKInitWithDesc(RKRadarDesc);
RKRadar *RKInitLean(void);               // For a lean system, PX-1000 like
RKRadar *RKInitMean(void);               // For a medium system, RaXPol like
RKRadar *RKInitFull(void);               // For a high-performance system, PX-10,000 like
RKRadar *RKInit(void);                   // Everything based on default settings, in between mean & lean
int RKFree(RKRadar *radar);

Properties

Hardware hooks are provided to communicate with a digital transceiver, a positioner and various sensors. They must obey the protocol to implement three important functions: init, exec and free routines. These functions will be called to start the hardware routine, execute text form commands that will be passed down the master controller, and to deallocate the resources properly upon exit, respectively.

// Set the transceiver. Pass in function pointers: init, exec and free
int RKSetTransceiver(RKRadar *,
                     void *initInput,
                     RKTransceiver initRoutine(RKRadar *, void *),
                     int execRoutine(RKTransceiver, const char *, char *),
                     int freeRoutine(RKTransceiver));

// Set the pedestal. Pass in function pointers: init, exec and free
int RKSetPedestal(RKRadar *,
                  void *initInput,
                  RKPedestal initRoutine(RKRadar *, void *),
                  int execRoutine(RKPedestal, const char *, char *),
                  int freeRoutine(RKPedestal));

// Set the health relay. Pass in function pointers: init, exec and free
int RKSetHealthRelay(RKRadar *,
                     void *initInput,
                     RKHealthRelay initRoutine(RKRadar *, void *),
                     int execRoutine(RKHealthRelay, const char *, char *),
                     int freeRoutine(RKHealthRelay));

// Some states of the radar
int RKSetVerbose(RKRadar *radar, const int verbose);
int RKSetProcessingCoreCounts(RKRadar *radar,
                              const unsigned int pulseCompressionCoreCount,
                              const unsigned int momentProcessorCoreCount);

// Some operating parameters
int RKSetWaveform(RKRadar *radar, RKWaveform *waveform, const int group);
int RKSetWaveformToImpulse(RKRadar *radar);
uint32_t RKGetPulseCapacity(RKRadar *radar);

Interactions

// The radar engine state
int RKGoLive(RKRadar *);
int RKWaitWhileActive(RKRadar *);
int RKStop(RKRadar *);

// Positions
RKPosition *RKGetVacantPosition(RKRadar *);
void RKSetPositionReady(RKRadar *, RKPosition *);

// Pulses
RKPulse *RKGetVacantPulse(RKRadar *);
void RKSetPulseHasData(RKRadar *, RKPulse *);
void RKSetPulseReady(RKRadar *, RKPulse *);

// Rays
RKRay *RKGetVacantRay(RKRadar *);
void RKSetRayReady(RKRadar *, RKRay *);

// Health
RKHealth *RKGetVacantHealth(RKRadar *, RKHeathNode);
void RKSetHealthReady(RKRadar *, RKHealth *);

Hardware Routines

As mentioend previously, the initialization, execution and deallocation routines of the transceiver, pedestal, and health relay must have a strict form, as follows. The intialization of the hardware must be in the form of

RKTransceiver initRoutine(RKRadar *, void *);
RKPedestal    initRoutine(RKRadar *, void *);
RKHealthRelay initRoutine(RKRadar *, void *);

while the execution of command and the return of response must be in the form of

int execRoutine(RKTransceiver, const char *command, char *response);
int execRoutine(RKPedestal, const char *command, char *response);
int execRoutine(RKHealthRelay, const char *command, char *response);

and finally, the resource free routine must be in the form of

int freeRoutine(RKTransceiver);
int freeRoutine(RKPedestal);
int freeRoutine(RKHealthRelay);

Here is a simple example of execution routine of a transceiver that response to a PRT change

int execRoutine(RKTransceiver userTransceiver, const char *command, char *response) {
    // Type cast it to your defined type
    UserTransceiverStruct transceiver = (UserTransceiverStruct *)userTransceiver;
    
    // Restore the radar reference.
    RKRadar *radar = transceiver->radar;
    
    // Do something with the instruction, say change the prt
    float prt;
    char dummy[64];
    if (!strcmp(command, "prt")) {
        sscanf(command, "%s %f", dummy, &prt);
        transceiver->prt = prt;
        sprintf(response, "ACK. Command executed.");
    }
    return 0;
}

Reserved Keywords for Commands

disconnect

This is a command the master controller issues when everything should stop.

state

This is a command the master controller issues for checking if the component wants to report opereate (1) or standby (0)

RadarKit Test Program

A test program is provided to assess if everything can run properly with your system. Call it with a help option to show all the available options.

rktest --help