DSP module

The DSP module is a wrapper to a series of Digital Signal Processing functions.

DSP.tone

Creates a IQData object with a pure CW tone (complex samples) of 'frequency_in_Hz' sampled at 'sample_rate_hz'.

If the frequency is 0, then the generated signal is a constant with real part=1 (or optional amplitude level), complex part=0.

Object IQData = DSP.tone( number frequency_in_Hz, number samples_to_create, number sample_rate_hz , [optional amplitude, number phase_shift_degrees])

Optional parameters :

  • Amplitude : between 0 and 1,
  • Phase : start with the given input phase, in degrees.

Examples

Create audio WAV file (48000 Hz , 2 channels) :

var IQ = DSP.tone(440, 80000, 48e3);
IQ.dump();
IQ.saveToFile('/tmp/tone.wav');

Create IQ file (Complex Float simple precision) :

var IQ = DSP.tone(440, 1000, 2e6);
IQ.dump();
IQ.saveToFile('/opt/search/tone.cf32');

- generates a 440 Hz complex tone for 1000 samples in a signal sampled at 2MHz and save to file.

DSP.noiseTest

This function estimates the similarity of a given input IQ with a random signal. The result is a number between 0 and 100 estimating how many samples do not fit with a random distribution.

var IQ = ...
var test = DSP.noiseTest( IQ );
  • Low values implies very similar to random noise,
  • High values implies no similarity.

Note

Detailed specification of this algorithm only available for registered customers.

DSP.offset

Estimate the center frequency of the input signal, assuming it contains a mono-carrier modulated signal.

IQData output = DSP.offset( IQData input , [option channel])

Example:

var ffreqOffset = DSP.offset(rfiq);
var rfCentered = DSP.shift(rfiq, ffreqOffset)

DSP.shift

Applies a frequency shift to the input IQData

IQData out = DSP.shift( IQData in, frequency_hz , [option channel])

DSP.musicDOA

Note

This feature is available only with a License - Contact us for more details

Computes the angle of arrival for a signal sampled simultaneously on two antennas.

var DOA = DSP.musicDOA( IQData in,  number antenna_separation_meters);

Arguments:

  • IQData in : the IQ samples block, it must contains at least two channels (see how to use the setRFInputChannel function in DDCBank). Computation is limited to the first 1024 samples of the first two channels. The first channel is the reference channel.
  • number antenna_separation_meters : distance in meters between the two antennas.

This function implements the MUSIC DOA algorithm (see Wikipedia section). Results are in degrees.

Returns:

{
   "data_type":"music_doa",
   "timestamp": millisecs,
   "sample_rate": sample rate,
   "center_frequency_mhz": center frequency,
   "doa_deg": mot probable value,
   "doa_estimation":[
    {"value": likelihood at this angle,"angle":-90 },
    {"value": likelihood at this angle,"angle":-89 },
    ...
    {"value": likelihood at this angle,"angle": 0 },
    ..
    {"value": likelihood at this angle,"angle":90}
    ],
    "position":
    {"gps_fix":false,
    "latitude_N":0,
    "longitude_E":0,
    "altitude":0}}
}

Example :

  {"data_type":"music_doa",
  "timestamp":1686036580166,
  "sample_rate":50000,
  "center_frequency_mhz":868.099968,
  "doa_deg":-34,"doa_estimation":[
    {"value":-26.52569007873535,"angle":-90.00000250447816},{"value":-26.523197174072266,"angle":-89.00000250447818},
    {"value":-27.091938018798828,"angle":84.99999749552182},{"value":-27.071916580200195,"angle":85.99999749552184},{"value":-27.05628204345703,"angle":86.99999749552185},
    ....,
    {"value":-27.045076370239258,"angle":87.99999749552187},{"value":-27.038341522216797,"angle":88.99999749552184}
    ],
    "position":{
      "gps_fix":false,"latitude_N":0,"longitude_E":0,"altitude":0
    }
}

Minimalist code to capture a 2 channel IQ and process it :

var rx = BladeRF.makeDevice({"device_name" : "bladerf"});
rx.setRxCenterFreq(868);
rx.setRxSampleRate(5e6);
rx.setGain( 40 ) ;
rx.dump();


var bank = new DDCBank(rx,1);
bank.setRFInputChannel(-1);
var channel = bank.createChannel( 50e3 );
channel.setOffset( 0.1e6 );
if( channel.start() == false ) {
    print(' error starting channel id : ' + channel_name );
    exit();
}
for(;;) {
    var iq = channel.getIQ(true);
    var doa = DSP.musicDOA( iq, 0.170 ) ;

    print( JSON.stringify(doa) ) ;

}

DSP.powerFFT

Computes the complex modulus of FFT( in ).

Samples out = DSP.powerFFT( IQData in, fft_len, start_at, overlap, [option:channel on IQData input] )

Arguments:

  • IQData in : the IQ samples block
  • fft_len : a number, even, to be used for the FFT
  • start_at : first sample to consider (fft will start at in[start_at])
  • overlap :
    • if 0 : the FFT input is set as in[ current_position, ... , current_position+fft_len-1]
    • otherwise FFT input is set as in[ current_position, ... , current_position+fft_len-overlap]

Example:

var rx = Soapy.makeDevice( {'query' : 'driver=rtlsdr' });
if( typeof rx != 'object' ) {
    print('no radio ?');
    exit();
}

if( !rx.isValid()) {
    print('no radio ?');
    exit();
}


// set sample rate
if( rx.setRxSampleRate( 2048e3 )) {
    print('Sample rate changed');
}
rx.setGain(40);
rx.setRxCenterFreq( 466 );

// capture a block
var fft_size = 1024 ;
var samples = rx.Capture( fft_size*1024 ) ;
samples.dump();

var fft = DSP.powerFFT( samples, fft_size,0,128);
var lines = fft.getLength() / fft_size ;
for( var l = 0 ; l < lines ; l++ ) {
    // get the FFT result
    fft.getSamples( l*fft_size, power );
  // do something with data
    for( var x=0 ; x < fft_size ; x++ ) {
         var level = power[x] ;
       ...
  }
}

DSP.square

Compute the squared values of the input complex samples.

IQData out = DSP.square( IQData in , [option channel]);

DSP.quadric

IQData out = DSP.quadric( IQData in , [option channel]); 

DSP.filter

Calculates and applies a FIR filter to the input signal. The cut-off frequency is given in Hz. Output can be decimated by an integer factor.

IQData out = DSP.filter( IQData in, cutoff_Hz, int decimateby, lofreq HZ) 

DSP.resample

Resample IQ datas : ratio = out sr / in sr. The ratio can be fractional.

  DSP.resample(IQdata_in, ratio, [option channel]);
  • Example
IQData out = DSP.resample( IQData in, ratio = out sr / in sr );

DSP.decompose

Applies a frequency shift of lofreq and decompose in NBands the input signal. Each output band has a sampling rate of input_sampling_rate/NBands. Internally, the VM buils a Polyphase Filter Bank and decomposes the input signal after mixing with lo.

Array of IQData = DSP.decompose(IQData in, NBands, lofreq , [option channel])
  • Example
var samples = new IQData('');
if( !samples.loadFromFile('/files/SR14Ms.cs16') ) {
    print('Input file not found !');
    exit();
}

samples.setSampleRate(14e6);
// decompose in 14 bands
var channels = DSP.decompose( samples, 14, 0);
for( var i=0 ; i < channels.length ; i++ ) {
     var IQ = channels[i] ;
     ...
}

DSP.fmdemod

Performs FM/Phase demodulation from the given input IQData

DSP.fmdemod(IQData in, [option channel]);

DSP.rmsprofile

This function computes the RMS powerlevel in dB over a series of blocks.

  • The input IQData is split in blocks of 'window length samples',
  • For each block the RMS power level is computed
  • The result is returned as a FloatData object.
   var out = DSP.rmsprofile( IQData in , window length samples);

Example :

var x = DSP.rmsprofile( o, 1000);
var n = x.getLength();
var data = new Float32Array(n);
x.getSamples(0,data);
for( var i=0 ; i < n ; i++) {
     print( data[i]);
}

Example DSP

  • BPSK/QPSK analysis, IQ recording
var rx = Soapy.makeDevice( {'query' : 'driver=rtlsdr' });
var samples = rx.captureSubBand( samples, offset, sub_band_Hz );
samples.saveToFile('/tmp/samples.cf32');

var quadric = DSP.quadric( samples );
var square = DSP.square( samples );


// BPSK/QPSK peaks (JSON-type objects)
var sps2 = square.getPowerSpectrum( 1024 ) ;
var sps4 = quadric.getPowerSpectrum( 1024 ) ;

Last update: June 6, 2023