RSP2 Scanning Application for Pi2

Add useful snippets of code or links to entire SDR projects.
Post Reply
StevenP
Posts: 3
Joined: Tue Jan 16, 2018 4:26 pm

RSP2 Scanning Application for Pi2

Post by StevenP » Fri Jan 19, 2018 10:50 pm

I've been developing a C++ program that scans and stores frequency and power information. Eventually it is going to be put on a drone for RF surveillance. In my code, which is admittedly very much in the alpha stage, I'm running into a USB Buffer Overflow Error when executing it on the Pi. Could you point me in the direction of a possible solution?

Code: Select all

#include <unistd.h>
#include <semaphore.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <sstream>
#include <iostream>
#include <fstream>
#include <sstream>
#include <fftw3.h>
#include <math.h>
#include <algorithm>
#include <vector>
#include <sqlite3.h>
#include "mirsdrapi-rsp.h"

static int do_exit = 0;


sem_t s; 						// Semaphore 1
void *cbContext = NULL;			// SDR Callback context variable initially set to null
short *i_buffer;				// Pointers to where i data will be stored
short *q_buffer;				// Pointers to where q data will be stored
unsigned int _firstSample;		// index of first sample in packet buffer received from SDR
int _samplesPerPacket;			// samples per packet from SDR based on samplerate
int _grChanged; 				// gain reduction setting from callback function
int _fsChanged; 				// sample frequency setting from callback function
int _rfChanged;					// center frequency setting from callback function
int _firstcallback = 1;			// variable for tracking initial callback function


std::vector<double> _spectrum;								// power spectrum vector
std::vector<double> _freq_index;							// frequency index vector
std::string database = "/home/pi/raven/Velociraptor.db";  	// database path variable for sqlite

void callback(short *xi, short *xq, unsigned int firstSampleNum, int grChanged, int rfChanged,
			  int fsChanged, unsigned int numSamples, unsigned int reset, void *cbContext)
{

	// Set global variables to data received in callback function
	_firstSample = firstSampleNum;
	_samplesPerPacket = numSamples;
	_grChanged = grChanged;
	_rfChanged = rfChanged;
	_fsChanged = fsChanged;

	
	// allocate appropriate amount of memory for IQ buffer if first callback
	if(_firstcallback == 1)
	{
		i_buffer = (short*)malloc(numSamples * sizeof(short));
		q_buffer = (short*)malloc(numSamples * sizeof(short));
		_firstcallback = 0;

	}

	// Write iq data to buffer
	for (int i = 0; i < numSamples; i++)
	{
		i_buffer[i] = xi[i];
		q_buffer[i] = xq[i];
	}

	// signal sempahore 1
	sem_post(&s);

	return;
}

static int callbackDB(void *NotUsed, int argc, char **argv, char **azColName)
{
	int i;
	for(i = 0; i<argc; i++)
	{
		printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
	}
	printf("\n");
	return 0;
}

void callbackGC(unsigned int grdB, unsigned int lnaGRdB, void *cbContext)
{
	// do something with updated gain information
	// NOT USED CURRENTLY
	return;
}

void FFT(short *ij, short *qj)
{
	// FFTW Variable Initialization
	const int N = _samplesPerPacket;				// Number of bins in data being passed to FFT, int type
	const double NFFT = (double)_samplesPerPacket;	// Number of bins in data being passed to FFT, double type
	const double fs = 2.048;						// sample rate, hardcoded
	double rI[N] = {0};								// raw I data storage
	double rQ[N] = {0};								// raw Q data storage
	double fft_out[N] = {0};						// Array of doubles to store output of FFT
	double p, q, x, z; 								// Variables used in math below
	fftw_complex *in, *out; 						// FFTW input/output and spectrum variables
	fftw_plan plan; 								// FFTW plan variable


	in = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * N);			// Allocate memory for FFTW input based on size of input
	out = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * N);		// Allocate memory for FFTW output based on size of input
	plan = fftw_plan_dft_1d(N, in, out, FFTW_FORWARD, FFTW_ESTIMATE);	// Set FFTW plan parameters


	// First, convert values in xi and xq to double, and store in rI and rQ.
	for (int i = 0; i < _samplesPerPacket; i++)
    {

        rI[i] = (double)ij[i];
        rQ[i] = (double)qj[i];

	}

	// Second, divide all values which are now doubles by 2^12 (Since SDR Play has a 12 bit ADC). Store new values in FFTW input array
	for(int e = 0; e < N; ++e)
	{
		in[e][0] = rI[e]/4096; // 2^12
		in[e][1] = rQ[e]/4096; // 2^12
	}


	// Third, execute the FFT
	fftw_execute(plan); 

	// Fourth, perform necessary math to get power information in dBm
	for(int e = 0; e < N; ++e)
	{
		// Divide every value by size of FFT (NFFT)
		p = out[e][0];
		p = p / NFFT; 
		q = out[e][1];
		q = q / NFFT;

		// Combine 2D output into 1D by squaring and adding matching elements
		x = p*p + q*q;

		// Multiply by 2, and divide by the resistance
		x = x * 2 / 50; 		// 50 ohm resistance
		x = x / .001; 			// 10 ^ -3

		z = 10 * log10(x);

		// Adjust for LNA Gain Reduction of 48
		fft_out[e] = z - 48;
	}

	// Finally, add power values to the spectrum in order
	for (int i = 0; i < _samplesPerPacket; i++)
    {

		_spectrum.push_back (fft_out[i]);			

	}
	

	fftw_destroy_plan(plan); 	// Must be done everytime to prevent artifacting

	return;

}

int main(int argc, char **argv){

	int vel_id = 29;		// Velociraptor ID number
	int sps;				// Packet size variable used in ReInit API function
	int done = 0;			// Flag for completion of scan
	int syncUpdate = 0; 	// Initially use asynchronus updates
	int newGr = 0;			// Gain Reduction Initialization (0 dBm)
	int sysGr = 0;			// Gain Reduction Initialization (0 dBm)
	int debug_mode = 0;		// Debug mode (0=off, 1=on)
	float ver;				// Version of API, returned in ApiVersion API function
	double min_rf = 0;		// Lower bound of scan (MHz)
	double max_rf = 0;		// Upper Bound of scan (MHz)
	double threshold = 0;   // Power threshold for what is sent to database after scan is complete (dBm)
	double fs_in = 8.192;	// Sample frequency before downconverting (MHz)
	double fs_out = 2.048;	// Sample frequency after downconverting (MHz)
	double rf;				// Center frequency
	double lat = 0;			// Latitude
	double lon = 0;			// Longitude

	mir_sdr_ErrT err;											// Error message storage
	mir_sdr_Bw_MHzT bwType = mir_sdr_BW_1_536;					// Set IF Bandwidth to 1.536 MHz
	mir_sdr_If_kHzT ifType = mir_sdr_IF_2_048;					// Set IF Frequency to 2.048 MHz
	mir_sdr_SetGrModeT grMode = mir_sdr_USE_SET_GR;				// Set Gain Reduction mode to manual
	mir_sdr_ReasonForReinitT rfChange = mir_sdr_CHANGE_RF_FREQ;	// Reason for performing Reinitialization of the SDRPlay
	mir_sdr_LoModeT loMode = mir_sdr_LO_Undefined;				// LO mode not used

	sqlite3 *db;		// SQLite database pointer
	char *zErrMsg = 0;	// SQLite error message pointer
	int rc;				// SQLite return status
	std::ostringstream temp;
	std::string sql;

	// If scan settings are given, use them, otherwise use default settings.
	if (argc<5) {
		min_rf = 30; 		// scan lower bound (MHz)
		max_rf = 512; 		// scan upper bound (MHz)
		threshold = -70;	// no threshold
		debug_mode = 0;		// debug off

	}
	else {
		min_rf = atof(argv[1]); 	// scan lower bound (MHz)
		max_rf = atof(argv[2]); 	// scan upper bound (MHz)
		threshold = atof(argv[3]);	// threshold (dBm)
		debug_mode = atoi(argv[4]); // debug mode (1=on, 0=off)
	}

	double freq_resolution;			// initialize frequency resolution variable
	double freq = min_rf;			// start frequency index at lower bound

	// create semaphore
	sem_init(&s, 0, 0);

	// Check API version
	err = mir_sdr_ApiVersion(&ver);
	if (ver != MIR_SDR_API_VERSION)
	{
		fprintf(stderr, "API VERSION DOES NOT MATCH: Error %d\n", err);
		exit(1);
	}

	// enable debug output
	if (debug_mode == 1)
	{
		mir_sdr_DebugEnable(1);
	}
	

	// Determine initial frequency to tune to [Add fs/2 to the lower bound to get first center frequency]
	rf = min_rf + fs_out/2;

	// Initialize API and hardware
	err = mir_sdr_StreamInit(&newGr, fs_in, rf, bwType, ifType, 0, &sysGr, grMode, &sps, callback, callbackGC, (void *)NULL);
	if (err != mir_sdr_Success)
	{
		fprintf(stderr, "mir_sdr_StreamInit: Error %d\n", err);
		exit(1);
	}


	// TODO: Query GPS for location

	// Configure DC Tracking in tuner, this will perform DC offset correction when configured correctly
	err = mir_sdr_SetDcMode(4, 1); 		// 4 = One shot DC offset correction
										// 1 = Maximum speedup
	if (err != mir_sdr_Success)
	{
		fprintf(stderr, "mir_sdr_SetDCMode: Error %d\n", err);
		exit(1);
	}
	err = mir_sdr_SetDcTrackTime(63); 	// 63 = Tracking time in ms (0-63)
	if (err != mir_sdr_Success)
	{
		fprintf(stderr, "mir_sdr_SetDcTrackTime: Error %d\n", err);
		exit(1);
	}

	// Main Processing Loop
	while(!done)
	{
		
		sem_wait(&s);				// wait on semaphore for samples to be received in callback
		FFT(i_buffer, q_buffer);	// get data from buffer, pass it through FFT
		rf = rf + fs_out;			// move center frequency for next IQ collection (increment by fs)

		// update SDR center frequency by performing ReInit
		err = mir_sdr_Reinit(&newGr, fs_in, rf, bwType, ifType, loMode, 1, &sysGr, grMode, &sps, rfChange);
		if (err != mir_sdr_Success)
		{
			fprintf(stderr, "mir_sdr_Reinit: Error %d\n", err);
			exit(1);
		}

		// signal sempahore 2
		sem_post(&z);

		// TODO: Query GPS for location

		// set update period in IQ buffer
		err = mir_sdr_SetSyncUpdatePeriod(_samplesPerPacket);
	   	if (err != mir_sdr_Success)
		{
			fprintf(stderr, "mir_sdr_SetSyncUpdatePeriod: Error %d\n", err);
			exit(1);
		}
		// set sample to start update period from in IQ buffer
		err = mir_sdr_SetSyncUpdateSampleNum(_firstSample);
		if (err != mir_sdr_Success)
		{
			fprintf(stderr, "mir_sdr_SetSyncUpdateSampleNum: Error %d\n", err);
			exit(1);
		}
 
		syncUpdate = 1; 	// set syncupdate mode to synchronous
		
		// when current frequency exceeds max frequency, scan is complete
		if(rf > max_rf)
		{
			fprintf(stderr, "Scan Complete!\n");
			done = 1;	
		}
		
	}

	freq_resolution = fs_out / _samplesPerPacket;	// set freq resolution based on settings [fs/N]

	for (int i = 0; i < (int)_spectrum.size(); i++)
	{
		freq = freq + freq_resolution;						// create frequency indecies by incrementing freq with the freq resolution
		_freq_index.push_back (freq);						// populate frequency index array in order
	}


	// TEMPORARY LOGIC TO WRITE OUTPUT DIRECTLY TO CSV, WILL BE REMOVED ONCE DATABASE WRITING IS SUCCESSFULLY IMPLEMENTED
	std::ofstream outfile;
	outfile.open("spectrum.csv");
	for (int i = 0; i < (int)_spectrum.size(); i++)
	{
		// Threshold determines what gets written to database
		if (_spectrum[i] > threshold)
		{
			outfile << _freq_index[i] << "," << _spectrum[i];
			outfile << std::endl;
		}
	}
	outfile.close();
	

	// Database Writing using sqlite3
	rc = sqlite3_open("Velociraptor.db", &db);
	if(rc)
	{
		fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
		return(0);
	} else {
		fprintf(stderr, "Opened database successfully\n");
	}

	for (int i = 0; i < (int)_spectrum.size(); i++)
	{
		// Threshold determines what gets written to database
		if (_spectrum[i] > threshold)
		{
			temp.str("");
			temp << "INSERT INTO scan (id, freq, power, latitude, longitude) " << "VALUES(" << vel_id << ", " << _freq_index[i] << ", " << _spectrum[i] << ", " << lat << ", " << lon << " ); " ;
			sql = temp.str();
			rc = sqlite3_exec(db, sql.c_str(), callbackDB, 0, &zErrMsg);

			if(rc != SQLITE_OK)
			{
				fprintf(stderr, "SQL error: %s\n", zErrMsg);
				sqlite3_free(zErrMsg);
			}
					
		}
	}

	// At exit
	free(i_buffer);				// release memory allocated to i buffer
	free(q_buffer);				// release memory allocated to q buffer
	fprintf(stderr, "Uninitializing SDR and closing database...\n");
	sqlite3_close(db);
	mir_sdr_StreamUninit();
	fprintf(stderr, "Done\n");
	return 0;

}
Last edited by StevenP on Thu Jan 01, 1970 12:00 am, edited 0 times in total.
Reason: No reason

Post Reply