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;
}