此软件接收机采用c语言进行编写,使用GSL库,进行傅里叶分析,参考了“A Software-Defined GPS and Galileo Receiver",可以认为是GNSS_SDR的c语言版本。
测试环境为:ubuntu20.0.4
将软件接收机的输出结果,进行坐标反查,可得如下结果,可以看到位置解析正确
其中:acquistion.c文件实现对卫星信号的捕获工作,并将捕获结果保存在acquisitionResult结构体中,供后续的跟踪过程使用;
track.c文件实现对卫星信号的跟踪工作,并得到导航电文,跟踪结果保存在trackResult结构体中,供后续的星历计算,接收机位置结算使用;
postProceeding.c文件完成对接收机位置的计算
s a m p l e s P e r C o d e samplesPerCode samplesPerCode: 每一个扩频码,有多少个采样点
s e t t i n g s . s a m p l i n g F r e q / ( s e t t i n g s . c o d e F r e q B a s i s / s e t t i n g s . c o d e L e n g t h ) ) settings.samplingFreq\ /\ (settings.codeFreqBasis\ /\ settings.codeLength)) settings.samplingFreq / (settings.codeFreqBasis / settings.codeLength))
原理就是:一秒钟的所有采样点,除以一秒中的扩频码个数,得到一个扩频码中所含有的采样点数
需要使用11ms的数据进行后续的捕获操作: 其中1ms进行捕获码相位,其余10ms进行更精确的载波频率估计
关于利用fft来计算相关运算原理:
对于N点的 x [ n ] x\left[n\right] x[n],以及 h [ n ] h\left[n\right] h[n],对其进行相关运算可得:
y [ n ] = ∑ i = 0 N − 1 x [ i ] h ∗ [ i − n ] y\left[n\right] = \sum_{i=0}^{N-1}{x\left[i\right]h^\ast\left[i-n\right]} y[n]=∑i=0N−1x[i]h∗[i−n]
而对于离散序列的卷积运算:
y [ n ] = ∑ i = 0 N − 1 x [ i ] h [ n − i ] y\left[n\right]= \sum_{i=0}^{N-1}x\left[i\right]h\left[n-i\right] y[n]=∑i=0N−1x[i]h[n−i]
其对应的傅里叶变换为:
x ( j ω ) × h ( j ω ) x(j\omega)\times h(j\omega) x(jω)×h(jω)
如果将 x [ n ] x\left[n\right] x[n]和 h ∗ [ − n ] h^\ast\left[-n\right] h∗[−n]进行卷积运算:
y [ n ] = ∑ i = 0 N − 1 x [ i ] h ∗ [ − ( n − i ) ] y\left[n\right]=\sum_{i=0}^{N-1}{x\left[i\right]h^\ast\left[-\left(n-i\right)\right]} y[n]=∑i=0N−1x[i]h∗[−(n−i)]
可以看到其结果是和相关运算的结果相同的。
而其对应的傅里叶变换为:
x ( j ω ) × h ∗ ( j ω ) x(j\omega)\times h^\ast\left(j\omega\right) x(jω)×h∗(jω)
则对于两个信号的相关运算,可以将其转变为傅里叶变换进行求解
// 1. Read 2ms Data From the signal File
int8_t *v1 = (int8_t*)malloc(sizeof(int8_t)*sampleNumbersPerCode);
int8_t *v2 = (int8_t*)malloc(sizeof(int8_t)*sampleNumbersPerCode);
// 11ms data to estimate the fined frequency
int8_t *fineFreqDat = (int8_t*)malloc(sizeof(int8_t)*sampleNumbersPerCode*11);
fread(v1, sizeof(int8_t), sampleNumbersPerCode, fid);
fread(v2, sizeof(int8_t), sampleNumbersPerCode, fid);
fseek(fid, 0, SEEK_SET);
fread(fineFreqDat, sizeof(int8_t), sampleNumbersPerCode*11, fid);
// 2. Get prn code
double* codeData = (double*)malloc(sizeof(double)*sampleNumbersPerCode*2);
int8_t* caCodeSampling = (int8_t*)malloc(sizeof(int8_t)*sampleNumbersPerCode);
for (uint32_t i=0; i<(receiverSetting->acqStatelliteList)->size; i++) {
caCodeAfterSampling(receiverSetting, sampleNumbersPerCode, caCodeSampling, receiverSetting->acqStatelliteList->data[i]);
// pack int8_t to gsl_vector_complex
for (uint32_t i=0; i<sampleNumbersPerCode; i++) {
REAL(codeData,i) = *(caCodeSampling+i);
IMAG(codeData,i) = 0;
}
// calculate the fft of C/A code
gsl_fft_complex_wavetable* waveTable;
gsl_fft_complex_workspace* workSpace;
waveTable = gsl_fft_complex_wavetable_alloc(sampleNumbersPerCode);
workSpace = gsl_fft_complex_workspace_alloc(sampleNumbersPerCode);
gsl_fft_complex_forward(codeData, 1, sampleNumbersPerCode, waveTable, workSpace);
// conjugat the code fft result
for (uint32_t i=0; i<sampleNumbersPerCode; i++) {
IMAG(codeData,i) = -IMAG(codeData,i);
}
// check every possible frequency bin, step is 0.5Khz
double * sinData = (double*)malloc(sizeof(double)*sampleNumbersPerCode);
double * cosData = (double*)malloc(sizeof(double)*sampleNumbersPerCode);
double * i_data_base_1 = (double*)malloc(sizeof(double)*sampleNumbersPerCode);
double * q_data_base_1 = (double*)malloc(sizeof(double)*sampleNumbersPerCode);
double * complex_data_base_1 = (double*)malloc(sizeof(double)*2*sampleNumbersPerCode);
double * i_data_base_2 = (double*)malloc(sizeof(double)*sampleNumbersPerCode);
double * q_data_base_2 = (double*)malloc(sizeof(double)*sampleNumbersPerCode);
double * complex_data_base_2 = (double*)malloc(sizeof(double)*2*sampleNumbersPerCode);
double * multiplexed_fft_result_1 = (double*)malloc(sizeof(double)*2*sampleNumbersPerCode);
double * multiplexed_fft_result_2 = (double*)malloc(sizeof(double)*2*sampleNumbersPerCode);
double phasePerSamplePoint = 2*M_PI/receiverSetting->samplingFreq;
double **twoDimResult = (double**)malloc(sizeof(double*)*receiverSetting->acqSearchBand*2);
// store the acquisition result
for (int i=0; i<receiverSetting->acqSearchBand*2; i++) {
twoDimResult[i] = (double*)malloc(sizeof(double)*sampleNumbersPerCode);
}
for (int i=0; i<receiverSetting->acqSearchBand*2; i++) {
// frequency for now
double freqBin = receiverSetting->intermediatFreq - receiverSetting->acqSearchBand/2*1E3 + i*0.5*1E3;
// local oscillator
for (uint32_t i=0; i<sampleNumbersPerCode; i++) {
sinData[i] = sin(i*freqBin*phasePerSamplePoint);
cosData[i] = cos(i*freqBin*phasePerSamplePoint);
}
// downconversion to baseband
for (uint32_t i=0; i<sampleNumbersPerCode; i++) {
i_data_base_1[i] = sinData[i] * v1[i];
q_data_base_1[i] = cosData[i] * v1[i];
REAL(complex_data_base_1, i) = i_data_base_1[i];
IMAG(complex_data_base_1, i) = q_data_base_1[i];
i_data_base_2[i] = sinData[i] * v2[i];
q_data_base_2[i] = cosData[i] * v2[i];
REAL(complex_data_base_2, i) = i_data_base_2[i];
IMAG(complex_data_base_2, i) = q_data_base_2[i];
}
// Calculate the fft of the baseband signal
gsl_fft_complex_forward(complex_data_base_1, 1, sampleNumbersPerCode, waveTable, workSpace);
gsl_fft_complex_forward(complex_data_base_2, 1, sampleNumbersPerCode, waveTable, workSpace);
// execute the multiplication of fft result
for (uint32_t i=0; i<sampleNumbersPerCode; i++) {
REAL(multiplexed_fft_result_1, i) = REAL(codeData, i)*REAL(complex_data_base_1, i) - IMAG(codeData, i)*IMAG(complex_data_base_1, i);
IMAG(multiplexed_fft_result_1, i) = REAL(codeData, i)*IMAG(complex_data_base_1, i) + IMAG(codeData, i)*REAL(complex_data_base_1, i);
REAL(multiplexed_fft_result_2, i) = REAL(codeData, i)*REAL(complex_data_base_2, i) - IMAG(codeData, i)*IMAG(complex_data_base_2, i);
IMAG(multiplexed_fft_result_2, i) = REAL(codeData, i)*IMAG(complex_data_base_2, i) + IMAG(codeData, i)*REAL(complex_data_base_2, i);
}
// invert the fft result to get the corelation
gsl_fft_complex_backward(multiplexed_fft_result_1, 1, sampleNumbersPerCode, waveTable, workSpace);
gsl_fft_complex_backward(multiplexed_fft_result_2, 1, sampleNumbersPerCode, waveTable, workSpace);
// gsl's invert fft is portional to ture fft result
for (uint32_t i=0; i<sampleNumbersPerCode; i++) {
REAL(multiplexed_fft_result_1, i) = REAL(multiplexed_fft_result_1, i)/sampleNumbersPerCode;
IMAG(multiplexed_fft_result_1, i) = IMAG(multiplexed_fft_result_1, i)/sampleNumbersPerCode;
REAL(multiplexed_fft_result_2, i) = REAL(multiplexed_fft_result_2, i)/sampleNumbersPerCode;
IMAG(multiplexed_fft_result_2, i) = IMAG(multiplexed_fft_result_2, i)/sampleNumbersPerCode;
}
// find the max power bwtween multiplexed_fft_reslt_1 and multiplexed_fft_result2
double *abs_correlation_value_1, *abs_correlation_value_2;
double abs_correlation_value_max_1, abs_correlation_value_max_2;
abs_correlation_value_max_1 = abs_correlation_value_max_2 = 0;
abs_correlation_value_1 = (double*)malloc(sizeof(double)*sampleNumbersPerCode);
abs_correlation_value_2 = (double*)malloc(sizeof(double)*sampleNumbersPerCode);
for (uint32_t i=0; i<sampleNumbersPerCode; i++) {
abs_correlation_value_1[i] = REAL(multiplexed_fft_result_1, i)*REAL(multiplexed_fft_result_1, i)+IMAG(multiplexed_fft_result_1, i)*IMAG(multiplexed_fft_result_1, i);
abs_correlation_value_max_1 = abs_correlation_value_max_1 >= abs_correlation_value_1[i] ? abs_correlation_value_max_1 : abs_correlation_value_1[i];
abs_correlation_value_2[i] = REAL(multiplexed_fft_result_2, i)*REAL(multiplexed_fft_result_2, i)+IMAG(multiplexed_fft_result_2, i)*IMAG(multiplexed_fft_result_2, i);
abs_correlation_value_max_2 = abs_correlation_value_max_2 >= abs_correlation_value_2[i] ? abs_correlation_value_max_2 : abs_correlation_value_2[i];
}
if (abs_correlation_value_max_1>abs_correlation_value_max_2) {
memcpy(twoDimResult[i], abs_correlation_value_1, sizeof(double)*sampleNumbersPerCode);
} else {
memcpy(twoDimResult[i], abs_correlation_value_2, sizeof(double)*sampleNumbersPerCode);
}
free(abs_correlation_value_1);
free(abs_correlation_value_2);
}
// Free space
gsl_fft_complex_wavetable_free(waveTable);
gsl_fft_complex_workspace_free(workSpace);
// find the max point in the 2D plan
double max = 0;
struct acquisitionPoint acq;
for (int i=0; i<receiverSetting->acqSearchBand*2; i++) {
for (uint32_t j=0; j<sampleNumbersPerCode; j++) {
double temp = twoDimResult[i][j];
if (max<temp) {
max = temp;
acq.freqBin = i;
acq.codePhase = j;
acq.value = max;
}
}
}
// find the second max point in the 2D plan
uint32_t freqBin;
freqBin = acq.freqBin;
double secondMax = 0;
uint32_t startPoint, endPoint;
startPoint = endPoint = 0;
if (acq.codePhase <= sampleNumbersPerChip) {
startPoint = acq.codePhase + sampleNumbersPerChip;
endPoint = sampleNumbersPerCode - (sampleNumbersPerChip - acq.codePhase);
for (uint32_t i=startPoint; i<endPoint; i++) {
if (secondMax<twoDimResult[freqBin][i]) {
secondMax = twoDimResult[freqBin][i];
}
}
} else if (acq.codePhase+sampleNumbersPerChip >= sampleNumbersPerCode) {
startPoint = acq.codePhase+sampleNumbersPerChip - sampleNumbersPerCode;
endPoint = acq.codePhase-sampleNumbersPerChip;
for (uint32_t i=startPoint; i<endPoint; i++) {
if (secondMax<twoDimResult[freqBin][i]) {
secondMax = twoDimResult[freqBin][i];
}
}
} else {
for (uint32_t i=0; i<acq.codePhase-sampleNumbersPerChip; i++) {
if (secondMax<twoDimResult[freqBin][i]) {
secondMax = twoDimResult[freqBin][i];
}
}
for (uint32_t i=acq.codePhase+sampleNumbersPerChip; i<sampleNumbersPerCode; i++) {
if (secondMax<twoDimResult[freqBin][i]) {
secondMax = twoDimResult[freqBin][i];
}
}
}
if (max/secondMax >= receiverSetting->acqThreshold) {
printf("satellite %d is Acqitioned\n", i);
// fine the frequency
double meanValue, sum;
sum = meanValue = 0;
for (uint32_t i=0; i<sampleNumbersPerCode*11; i++) {
sum += fineFreqDat[i];
}
meanValue = sum/sampleNumbersPerCode/11;
uint32_t totalNumberForFFT = next2pow(sampleNumbersPerCode*10)*8;
double * paddingDataFFT = (double*)malloc(sizeof(double)*totalNumberForFFT*2);
double * absFFTValue = (double*)malloc(sizeof(double)*totalNumberForFFT);
int8_t * caCodeSamplingFor10MS = (int8_t*)malloc(sizeof(int8_t)*sampleNumbersPerCode*10);
// generate long CACode
caCodeAfterSampling(receiverSetting, sampleNumbersPerCode*10, caCodeSamplingFor10MS, receiverSetting->acqStatelliteList->data[i]);
memset(paddingDataFFT, 0, sizeof(double)*totalNumberForFFT*2);
for(uint32_t i=0; i<sampleNumbersPerCode*10; i++) {
REAL(paddingDataFFT, i) = (fineFreqDat[i+acq.codePhase]-meanValue)*caCodeSamplingFor10MS[i];
}
//FILE *fid_temp = fopen("paddingDataFFT.dat", "w");
//fwrite(paddingDataFFT, sizeof(double), totalNumberForFFT*2, fid_temp);
//fclose(fid_temp);
gsl_fft_complex_wavetable* fineWaveTable = gsl_fft_complex_wavetable_alloc(totalNumberForFFT);
gsl_fft_complex_workspace* fineWorkSpace = gsl_fft_complex_workspace_alloc(totalNumberForFFT);
gsl_fft_complex_forward(paddingDataFFT, 1, totalNumberForFFT, fineWaveTable, fineWorkSpace);
for (uint32_t i=0; i<totalNumberForFFT; i++) {
absFFTValue[i] = sqrt(REAL(paddingDataFFT, i)*REAL(paddingDataFFT, i) + IMAG(paddingDataFFT, i)*IMAG(paddingDataFFT, i));
}
free(paddingDataFFT);
gsl_fft_complex_wavetable_free(fineWaveTable);
gsl_fft_complex_workspace_free(fineWorkSpace);
uint32_t uniqueFreq = ceil((totalNumberForFFT+1.0)/2);
struct finedFreq finedFreqStructure;
finedFreqStructure.findFreqBin = 0;
finedFreqStructure.value = 0;
for (uint32_t i=5; i<uniqueFreq-5; i++) {
if (finedFreqStructure.value<absFFTValue[i]) {
finedFreqStructure.value = absFFTValue[i];
finedFreqStructure.findFreqBin = i;
}
}
acqResult[i].flag = 1;
acqResult[i].codePhase = acq.codePhase;
acqResult[i].freq = receiverSetting->samplingFreq/totalNumberForFFT*(finedFreqStructure.findFreqBin+1);
free(absFFTValue);
free(caCodeSamplingFor10MS);
for (int i=0; i<receiverSetting->acqSearchBand*2; i++) {
free(twoDimResult[i]);
}
free(twoDimResult);
} else {
printf("satellite %d isnot Acqitioned\n", i);
}
free(complex_data_base_1);
free(complex_data_base_2);
free(i_data_base_1);
free(q_data_base_1);
free(i_data_base_2);
free(q_data_base_2);
free(sinData);
free(cosData);
free(multiplexed_fft_result_1);
free(multiplexed_fft_result_2);
}
free(v1);
free(v2);
free(codeData);
free(caCodeSampling);
free(fineFreqDat);
}
无论是 D L L DLL DLL还是 P L L PLL PLL,都可以当作为一个二阶的系统
其传递函数为:
F ( s ) = 1 s × τ 2 s + 1 τ 1 F\left(s\right)=\frac{1}{s}\times\frac{\tau_2s+1}{\tau_1} F(s)=s1×τ1τ2s+1
对其进行反变换可以得到:
f ( t ) = τ 2 τ 1 σ ( t ) + 1 τ 1 u ( t ) f\left(t\right)=\frac{\tau_2}{\tau_1}\sigma\left(t\right)+\frac{1}{\tau_1}u\left(t\right) f(t)=τ1τ2σ(t)+τ11u(t)
可知当得到的误差信号经过环路滤波器的时候,发生的是卷积操作,即:
c o n ( e r r ( t ) , f ( t ) ) con\left(err\left(t\right),f\left(t\right)\right) con(err(t),f(t))
于是可得,调节NCO的量为:
τ 2 τ 1 e r r ( t ) + 1 τ 1 ∫ 0 t e r r ( τ ) d τ \frac{\tau_2}{\tau_1}err\left(t\right)+\frac{1}{\tau_1}\int_{0}^{t}err\left(\tau\right)d\tau τ1τ2err(t)+τ11∫0terr(τ)dτ
采用的积分时间为1ms,每1ms计算一次误差值err
c a r r N c o = o l d C a r r N c o + ( t a u 2 c a r r / t a u 1 c a r r ) ∗ ( c a r r E r r o r − o l d C a r r E r r o r ) + c a r r E r r o r ∗ ( P D I c a r r / t a u 1 c a r r ) carrNco=oldCarrNco+\left(tau2carr/tau1carr\right)\ast\left(carrError-oldCarrError\right)+carrError\ast\left(PDIcarr/tau1carr\right) carrNco=oldCarrNco+(tau2carr/tau1carr)∗(carrError−oldCarrError)+carrError∗(PDIcarr/tau1carr)
double codePhaseStep = codeFreq/samplingFreq;
//Make sure that the last sample is the first point in the next bit
uint32_t blksize = ceil((receiverSetting->codeLength-remCodePhase)/codePhaseStep);
//Here need deal more carefully ***************************************************************************
int8_t *rawSignal = (int8_t*)malloc(sizeof(int8_t)*blksize);
//Read data from signal file
size_t sampleRead = fread(rawSignal, sizeof(int8_t), blksize, fid);
if(sampleRead != blksize) {
printf("Not able to read the specified number of samples for tracking, exiting!\n");
fclose(fid);
exit(-1);
}
//generate E P L code
CACode(receiverSetting->acqStatelliteList->data[i], caCodeStandard);
int8_t * earlyCode = (int8_t*)malloc(sizeof(int8_t)*blksize);
int8_t * promptCode = (int8_t*)malloc(sizeof(int8_t)*blksize);
int8_t * lateCode = (int8_t*)malloc(sizeof(int8_t)*blksize);
for(uint32_t i=0; i<blksize; i++) {
int32_t index = codePhaseStep*i+remCodePhase;
// Prompt code
promptCode[i] = caCodeStandard[index%receiverSetting->codeLength];
// Early code
index = floor(codePhaseStep*i+remCodePhase-earlyLateSpc);
if (index<0)
index = 1022;
earlyCode[i] = caCodeStandard[index%receiverSetting->codeLength];
// Late Code
index = floor(codePhaseStep*i+remCodePhase+earlyLateSpc);
lateCode[i] = caCodeStandard[index%receiverSetting->codeLength];
}
remCodePhase = codePhaseStep*(blksize)+remCodePhase - receiverSetting->codeLength;
// Generate the carrier frequency to mix the signal to baseband
double * carrCos, * carrSin;
carrCos = (double*)malloc(sizeof(double)*blksize);
carrSin = (double*)malloc(sizeof(double)*blksize);
for(uint32_t i=0; i<blksize; i++) {
carrCos[i] = cos(2*M_PI*carrFreq/samplingFreq*i + remCarrPhase);
carrSin[i] = sin(2*M_PI*carrFreq/samplingFreq*i + remCarrPhase);
}
remCarrPhase = remainder(remCarrPhase+(blksize)*2*M_PI*carrFreq/samplingFreq, 2*M_PI);
double* qBasebandSignal, *iBasebandSignal;
qBasebandSignal = (double*)malloc(sizeof(double)*blksize);
iBasebandSignal = (double*)malloc(sizeof(double)*blksize);
// Not be necessary
for (uint32_t i=0; i<blksize; i++) {
iBasebandSignal[i] = carrSin[i]*rawSignal[i];
qBasebandSignal[i] = carrCos[i]*rawSignal[i];
}
double I_E, Q_E, I_P, Q_P, I_L, Q_L;
I_E = Q_E = I_P = Q_P = I_L = Q_L = 0;
for (uint32_t i=0; i<blksize; i++) {
I_E = I_E + iBasebandSignal[i]*earlyCode[i];
Q_E = Q_E + qBasebandSignal[i]*earlyCode[i];
I_P = I_P + iBasebandSignal[i]*promptCode[i];
Q_P = Q_P + qBasebandSignal[i]*promptCode[i];
I_L = I_L + iBasebandSignal[i]*lateCode[i];
Q_L = Q_L + qBasebandSignal[i]*lateCode[i];
}
// Find PLL error and update carrier NCO
double carrError = atan(Q_P/I_P) / (2*M_PI);
double carrNco = oldCarrNco + (tau2carr/tau1carr) * (carrError-oldCarrError) + carrError *(PDIcarr/tau1carr);
oldCarrNco = carrNco;
oldCarrError = carrError;
//upated carrier freq
carrFreq = carrFreqBasis + carrNco;
(*trackResults)[i].carrFreq[j] = carrFreq;
//Find DLL error and update code NCO
double codeError = (sqrt(I_E*I_E+Q_E*Q_E)-sqrt(I_L*I_L+Q_L*Q_L)) / (sqrt(I_E*I_E+Q_E*Q_E)+sqrt(I_L*I_L+Q_L*Q_L));
double codeNco = oldCodeNco +(tau2code/tau1code)*(codeError-oldCodeError) + codeError *(PDIcode/tau1code);
oldCodeNco = codeNco;
oldCodeError = codeError;
//updated code freq
codeFreq = receiverSetting->codeFreqBasis - codeNco;
(*trackResults)[i].codeFreq[j] = codeFreq;
//store information
(*trackResults)[i].absoluteSamples[j] = ftell(fid);
(*trackResults)[i].dllDiscr[j] = codeError;
(*trackResults)[i].dllDiscrFilt[j] = codeNco;
(*trackResults)[i].pllDiscr[j] = carrError;
(*trackResults)[i].pllDiscrFilt[j] = carrNco;
(*trackResults)[i].I_E[j] = I_E;
(*trackResults)[i].Q_E[j] = Q_E;
(*trackResults)[i].I_P[j] = I_P;
(*trackResults)[i].Q_P[j] = Q_P;
(*trackResults)[i].I_L[j] = I_L;
(*trackResults)[i].Q_L[j] = Q_L;
// freee space
free(rawSignal);
free(earlyCode);
free(promptCode);
free(lateCode);
free(carrCos);
free(carrSin);
free(iBasebandSignal);
free(qBasebandSignal);
后续有时间的话,我会再将捕获和跟踪部分,做更加详细的说明,同时欢迎各位有能力和意愿的同学和我一块把这个接收机做的更加完善。
软件接收机地址