// // AUTOTEST : test multi-stage arbitrary resampler // void autotest_msresamp_crcf() { // options unsigned int m = 13; // filter semi-length (filter delay) float r=0.127115323f; // resampling rate (output/input) float As=60.0f; // resampling filter stop-band attenuation [dB] unsigned int n=1200; // number of input samples float fx=0.0254230646f; // complex input sinusoid frequency (0.2*r) //float bw=0.45f; // resampling filter bandwidth //unsigned int npfb=64; // number of filters in bank (timing resolution) unsigned int i; // number of input samples (zero-padded) unsigned int nx = n + m; // output buffer with extra padding for good measure unsigned int y_len = (unsigned int) ceilf(1.1 * nx * r) + 4; // arrays float complex x[nx]; float complex y[y_len]; // create resampler msresamp_crcf q = msresamp_crcf_create(r,As); // generate input signal float wsum = 0.0f; for (i=0; i<nx; i++) { // compute window float w = i < n ? kaiser(i, n, 10.0f, 0.0f) : 0.0f; // apply window to complex sinusoid x[i] = cexpf(_Complex_I*2*M_PI*fx*i) * w; // accumulate window wsum += w; } // resample unsigned int ny=0; unsigned int nw; for (i=0; i<nx; i++) { // execute resampler, storing in output buffer msresamp_crcf_execute(q, &x[i], 1, &y[ny], &nw); // increment output size ny += nw; } // clean up allocated objects msresamp_crcf_destroy(q); // // analyze resulting signal // // check that the actual resampling rate is close to the target float r_actual = (float)ny / (float)nx; float fy = fx / r; // expected output frequency // run FFT and ensure that carrier has moved and that image // frequencies and distortion have been adequately suppressed unsigned int nfft = 1 << liquid_nextpow2(ny); float complex yfft[nfft]; // fft input float complex Yfft[nfft]; // fft output for (i=0; i<nfft; i++) yfft[i] = i < ny ? y[i] : 0.0f; fft_run(nfft, yfft, Yfft, LIQUID_FFT_FORWARD, 0); fft_shift(Yfft, nfft); // run FFT shift // find peak frequency float Ypeak = 0.0f; float fpeak = 0.0f; float max_sidelobe = -1e9f; // maximum side-lobe [dB] float main_lobe_width = 0.07f; // TODO: figure this out from Kaiser's equations for (i=0; i<nfft; i++) { // normalized output frequency float f = (float)i/(float)nfft - 0.5f; // scale FFT output appropriately Yfft[i] /= (r * wsum); float Ymag = 20*log10f( cabsf(Yfft[i]) ); // find frequency location of maximum magnitude if (Ymag > Ypeak || i==0) { Ypeak = Ymag; fpeak = f; } // find peak side-lobe value, ignoring frequencies // within a certain range of signal frequency if ( fabsf(f-fy) > main_lobe_width ) max_sidelobe = Ymag > max_sidelobe ? Ymag : max_sidelobe; } if (liquid_autotest_verbose) { // print results printf(" desired resampling rate : %12.8f\n", r); printf(" measured resampling rate : %12.8f (%u/%u)\n", r_actual, ny, nx); printf(" peak spectrum : %12.8f dB (expected 0.0 dB)\n", Ypeak); printf(" peak frequency : %12.8f (expected %-12.8f)\n", fpeak, fy); printf(" max sidelobe : %12.8f dB (expected at least %.2f dB)\n", max_sidelobe, -As); } CONTEND_DELTA( r_actual, r, 0.01f ); // check actual output sample rate CONTEND_DELTA( Ypeak, 0.0f, 0.25f ); // peak should be about 0 dB CONTEND_DELTA( fpeak, fy, 0.01f ); // peak frequency should be nearly 0.2 CONTEND_LESS_THAN( max_sidelobe, -As ); // maximum side-lobe should be sufficiently low #if 0 // export results for debugging char filename[] = "msresamp_crcf_autotest.m"; FILE*fid = fopen(filename,"w"); fprintf(fid,"%% %s: auto-generated file\n",filename); fprintf(fid,"clear all;\n"); fprintf(fid,"close all;\n"); fprintf(fid,"r = %12.8f;\n", r); fprintf(fid,"nx = %u;\n", nx); fprintf(fid,"ny = %u;\n", ny); fprintf(fid,"nfft = %u;\n", nfft); fprintf(fid,"Y = zeros(1,nfft);\n"); for (i=0; i<nfft; i++) fprintf(fid,"Y(%3u) = %12.4e + j*%12.4e;\n", i+1, crealf(Yfft[i]), cimagf(Yfft[i])); fprintf(fid,"\n\n"); fprintf(fid,"%% plot frequency-domain result\n"); fprintf(fid,"f=[0:(nfft-1)]/nfft-0.5;\n"); fprintf(fid,"figure;\n"); fprintf(fid,"plot(f,20*log10(abs(Y)),'Color',[0.25 0.5 0.0],'LineWidth',2);\n"); fprintf(fid,"grid on;\n"); fprintf(fid,"xlabel('normalized frequency');\n"); fprintf(fid,"ylabel('PSD [dB]');\n"); fprintf(fid,"axis([-0.5 0.5 -120 20]);\n"); fclose(fid); printf("results written to %s\n",filename); #endif }
void *DemodulatorPreThread::threadMain() { #else void DemodulatorPreThread::threadMain() { #endif #ifdef __APPLE__ pthread_t tID = pthread_self(); // ID of this thread int priority = sched_get_priority_max( SCHED_FIFO) - 1; sched_param prio = {priority}; // scheduling priority of thread pthread_setschedparam(tID, SCHED_FIFO, &prio); #endif if (!initialized) { initialize(); } std::cout << "Demodulator preprocessor thread started.." << std::endl; std::deque<DemodulatorThreadPostIQData *> buffers; std::deque<DemodulatorThreadPostIQData *>::iterator buffers_i; std::vector<liquid_float_complex> in_buf_data; std::vector<liquid_float_complex> out_buf_data; // liquid_float_complex carrySample; // Keep the stream count even to simplify some demod operations // bool carrySampleFlag = false; terminated = false; while (!terminated) { DemodulatorThreadIQData *inp; iqInputQueue->pop(inp); bool bandwidthChanged = false; bool rateChanged = false; DemodulatorThreadParameters tempParams = params; if (!commandQueue->empty()) { while (!commandQueue->empty()) { DemodulatorThreadCommand command; commandQueue->pop(command); switch (command.cmd) { case DemodulatorThreadCommand::DEMOD_THREAD_CMD_SET_BANDWIDTH: if (command.llong_value < 1500) { command.llong_value = 1500; } if (command.llong_value > params.sampleRate) { tempParams.bandwidth = params.sampleRate; } else { tempParams.bandwidth = command.llong_value; } bandwidthChanged = true; break; case DemodulatorThreadCommand::DEMOD_THREAD_CMD_SET_FREQUENCY: params.frequency = tempParams.frequency = command.llong_value; break; case DemodulatorThreadCommand::DEMOD_THREAD_CMD_SET_AUDIO_RATE: tempParams.audioSampleRate = (int)command.llong_value; rateChanged = true; break; default: break; } } } if (inp->sampleRate != tempParams.sampleRate && inp->sampleRate) { tempParams.sampleRate = inp->sampleRate; rateChanged = true; } if (bandwidthChanged || rateChanged) { DemodulatorWorkerThreadCommand command(DemodulatorWorkerThreadCommand::DEMOD_WORKER_THREAD_CMD_BUILD_FILTERS); command.sampleRate = tempParams.sampleRate; command.audioSampleRate = tempParams.audioSampleRate; command.bandwidth = tempParams.bandwidth; command.frequency = tempParams.frequency; workerQueue->push(command); } if (!initialized) { continue; } // Requested frequency is not center, shift it into the center! if ((params.frequency - inp->frequency) != shiftFrequency || rateChanged) { shiftFrequency = params.frequency - inp->frequency; if (abs(shiftFrequency) <= (int) ((double) (wxGetApp().getSampleRate() / 2) * 1.5)) { nco_crcf_set_frequency(freqShifter, (2.0 * M_PI) * (((double) abs(shiftFrequency)) / ((double) wxGetApp().getSampleRate()))); } } if (abs(shiftFrequency) > (int) ((double) (wxGetApp().getSampleRate() / 2) * 1.5)) { continue; } // std::lock_guard < std::mutex > lock(inp->m_mutex); std::vector<liquid_float_complex> *data = &inp->data; if (data->size() && (inp->sampleRate == params.sampleRate)) { int bufSize = data->size(); if (in_buf_data.size() != bufSize) { if (in_buf_data.capacity() < bufSize) { in_buf_data.reserve(bufSize); out_buf_data.reserve(bufSize); } in_buf_data.resize(bufSize); out_buf_data.resize(bufSize); } in_buf_data.assign(inp->data.begin(), inp->data.end()); liquid_float_complex *in_buf = &in_buf_data[0]; liquid_float_complex *out_buf = &out_buf_data[0]; liquid_float_complex *temp_buf = NULL; if (shiftFrequency != 0) { if (shiftFrequency < 0) { nco_crcf_mix_block_up(freqShifter, in_buf, out_buf, bufSize); } else { nco_crcf_mix_block_down(freqShifter, in_buf, out_buf, bufSize); } temp_buf = in_buf; in_buf = out_buf; out_buf = temp_buf; } DemodulatorThreadPostIQData *resamp = NULL; for (buffers_i = buffers.begin(); buffers_i != buffers.end(); buffers_i++) { if ((*buffers_i)->getRefCount() <= 0) { resamp = (*buffers_i); break; } } if (resamp == NULL) { resamp = new DemodulatorThreadPostIQData; buffers.push_back(resamp); } int out_size = ceil((double) (bufSize) * iqResampleRatio) + 512; if (resampledData.size() != out_size) { if (resampledData.capacity() < out_size) { resampledData.reserve(out_size); } resampledData.resize(out_size); } unsigned int numWritten; msresamp_crcf_execute(iqResampler, in_buf, bufSize, &resampledData[0], &numWritten); resamp->setRefCount(1); resamp->data.assign(resampledData.begin(), resampledData.begin() + numWritten); // bool uneven = (numWritten % 2 != 0); // if (!carrySampleFlag && !uneven) { // resamp->data.assign(resampledData.begin(), resampledData.begin() + numWritten); // carrySampleFlag = false; // } else if (!carrySampleFlag && uneven) { // resamp->data.assign(resampledData.begin(), resampledData.begin() + (numWritten-1)); // carrySample = resampledData.back(); // carrySampleFlag = true; // } else if (carrySampleFlag && uneven) { // resamp->data.resize(numWritten+1); // resamp->data[0] = carrySample; // memcpy(&resamp->data[1],&resampledData[0],sizeof(liquid_float_complex)*numWritten); // carrySampleFlag = false; // } else if (carrySampleFlag && !uneven) { // resamp->data.resize(numWritten); // resamp->data[0] = carrySample; // memcpy(&resamp->data[1],&resampledData[0],sizeof(liquid_float_complex)*(numWritten-1)); // carrySample = resampledData.back(); // carrySampleFlag = true; // } resamp->audioResampleRatio = audioResampleRatio; resamp->audioResampler = audioResampler; resamp->audioSampleRate = params.audioSampleRate; resamp->stereoResampler = stereoResampler; resamp->firStereoLeft = firStereoLeft; resamp->firStereoRight = firStereoRight; resamp->iirStereoPilot = iirStereoPilot; resamp->sampleRate = params.bandwidth; iqOutputQueue->push(resamp); } inp->decRefCount(); if (!workerResults->empty()) { while (!workerResults->empty()) { DemodulatorWorkerThreadResult result; workerResults->pop(result); switch (result.cmd) { case DemodulatorWorkerThreadResult::DEMOD_WORKER_THREAD_RESULT_FILTERS: msresamp_crcf_destroy(iqResampler); if (result.iqResampler) { iqResampler = result.iqResampler; iqResampleRatio = result.iqResampleRatio; } if (result.firStereoLeft) { firStereoLeft = result.firStereoLeft; } if (result.firStereoRight) { firStereoRight = result.firStereoRight; } if (result.iirStereoPilot) { iirStereoPilot = result.iirStereoPilot; } if (result.audioResampler) { audioResampler = result.audioResampler; audioResampleRatio = result.audioResamplerRatio; stereoResampler = result.stereoResampler; } if (result.audioSampleRate) { params.audioSampleRate = result.audioSampleRate; } if (result.bandwidth) { params.bandwidth = result.bandwidth; } if (result.sampleRate) { params.sampleRate = result.sampleRate; } break; default: break; } } } } while (!buffers.empty()) { DemodulatorThreadPostIQData *iqDataDel = buffers.front(); buffers.pop_front(); delete iqDataDel; } DemodulatorThreadCommand tCmd(DemodulatorThreadCommand::DEMOD_THREAD_CMD_DEMOD_PREPROCESS_TERMINATED); tCmd.context = this; threadQueueNotify->push(tCmd); std::cout << "Demodulator preprocessor thread done." << std::endl; #ifdef __APPLE__ return this; #endif }
void SpectrumVisualProcessor::process() { if (!isOutputEmpty()) { return; } if (!input || input->empty()) { return; } if (fftSizeChanged.load()) { setup(newFFTSize); fftSizeChanged.store(false); } DemodulatorThreadIQData *iqData; input->pop(iqData); if (!iqData) { return; } //Start by locking concurrent access to iqData std::lock_guard < std::recursive_mutex > lock(iqData->getMonitor()); //then get the busy_lock std::lock_guard < std::mutex > busy_lock(busy_run); bool doPeak = peakHold.load() && (peakReset.load() == 0); if (fft_result.size() != fftSizeInternal) { if (fft_result.capacity() < fftSizeInternal) { fft_result.reserve(fftSizeInternal); fft_result_ma.reserve(fftSizeInternal); fft_result_maa.reserve(fftSizeInternal); fft_result_peak.reserve(fftSizeInternal); } fft_result.resize(fftSizeInternal); fft_result_ma.resize(fftSizeInternal); fft_result_maa.resize(fftSizeInternal); fft_result_temp.resize(fftSizeInternal); fft_result_peak.resize(fftSizeInternal); } if (peakReset.load() != 0) { peakReset--; if (peakReset.load() == 0) { for (unsigned int i = 0, iMax = fftSizeInternal; i < iMax; i++) { fft_result_peak[i] = fft_floor_maa; } fft_ceil_peak = fft_floor_maa; fft_floor_peak = fft_ceil_maa; } } std::vector<liquid_float_complex> *data = &iqData->data; if (data && data->size()) { unsigned int num_written; long resampleBw = iqData->sampleRate; bool newResampler = false; int bwDiff; if (is_view.load()) { if (!iqData->sampleRate) { iqData->decRefCount(); return; } while (resampleBw / SPECTRUM_VZM >= bandwidth) { resampleBw /= SPECTRUM_VZM; } resamplerRatio = (double) (resampleBw) / (double) iqData->sampleRate; size_t desired_input_size = fftSizeInternal / resamplerRatio; this->desiredInputSize.store(desired_input_size); if (iqData->data.size() < desired_input_size) { // std::cout << "fft underflow, desired: " << desired_input_size << " actual:" << input->data.size() << std::endl; desired_input_size = iqData->data.size(); } if (centerFreq != iqData->frequency) { if ((centerFreq - iqData->frequency) != shiftFrequency || lastInputBandwidth != iqData->sampleRate) { if (abs(iqData->frequency - centerFreq) < (wxGetApp().getSampleRate() / 2)) { long lastShiftFrequency = shiftFrequency; shiftFrequency = centerFreq - iqData->frequency; nco_crcf_set_frequency(freqShifter, (2.0 * M_PI) * (((double) abs(shiftFrequency)) / ((double) iqData->sampleRate))); if (is_view.load()) { long freqDiff = shiftFrequency - lastShiftFrequency; if (lastBandwidth!=0) { double binPerHz = double(lastBandwidth) / double(fftSizeInternal); unsigned int numShift = floor(double(abs(freqDiff)) / binPerHz); if (numShift < fftSizeInternal/2 && numShift) { if (freqDiff > 0) { memmove(&fft_result_ma[0], &fft_result_ma[numShift], (fftSizeInternal-numShift) * sizeof(double)); memmove(&fft_result_maa[0], &fft_result_maa[numShift], (fftSizeInternal-numShift) * sizeof(double)); // memmove(&fft_result_peak[0], &fft_result_peak[numShift], (fftSizeInternal-numShift) * sizeof(double)); // memset(&fft_result_peak[fftSizeInternal-numShift], 0, numShift * sizeof(double)); } else { memmove(&fft_result_ma[numShift], &fft_result_ma[0], (fftSizeInternal-numShift) * sizeof(double)); memmove(&fft_result_maa[numShift], &fft_result_maa[0], (fftSizeInternal-numShift) * sizeof(double)); // memmove(&fft_result_peak[numShift], &fft_result_peak[0], (fftSizeInternal-numShift) * sizeof(double)); // memset(&fft_result_peak[0], 0, numShift * sizeof(double)); } } } } } peakReset.store(PEAK_RESET_COUNT); } if (shiftBuffer.size() != desired_input_size) { if (shiftBuffer.capacity() < desired_input_size) { shiftBuffer.reserve(desired_input_size); } shiftBuffer.resize(desired_input_size); } if (shiftFrequency < 0) { nco_crcf_mix_block_up(freqShifter, &iqData->data[0], &shiftBuffer[0], desired_input_size); } else { nco_crcf_mix_block_down(freqShifter, &iqData->data[0], &shiftBuffer[0], desired_input_size); } } else { shiftBuffer.assign(iqData->data.begin(), iqData->data.begin()+desired_input_size); } if (!resampler || resampleBw != lastBandwidth || lastInputBandwidth != iqData->sampleRate) { float As = 60.0f; if (resampler) { msresamp_crcf_destroy(resampler); } resampler = msresamp_crcf_create(resamplerRatio, As); bwDiff = resampleBw-lastBandwidth; lastBandwidth = resampleBw; lastInputBandwidth = iqData->sampleRate; newResampler = true; peakReset.store(PEAK_RESET_COUNT); } unsigned int out_size = ceil((double) (desired_input_size) * resamplerRatio) + 512; if (resampleBuffer.size() != out_size) { if (resampleBuffer.capacity() < out_size) { resampleBuffer.reserve(out_size); } resampleBuffer.resize(out_size); } msresamp_crcf_execute(resampler, &shiftBuffer[0], desired_input_size, &resampleBuffer[0], &num_written); if (num_written < fftSizeInternal) { memcpy(fftInData, resampleBuffer.data(), num_written * sizeof(liquid_float_complex)); memset(&(fftInData[num_written]), 0, (fftSizeInternal-num_written) * sizeof(liquid_float_complex)); } else { memcpy(fftInData, resampleBuffer.data(), fftSizeInternal * sizeof(liquid_float_complex)); } } else { this->desiredInputSize.store(fftSizeInternal); num_written = data->size(); if (data->size() < fftSizeInternal) { memcpy(fftInData, data->data(), data->size() * sizeof(liquid_float_complex)); memset(&fftInData[data->size()], 0, (fftSizeInternal - data->size()) * sizeof(liquid_float_complex)); } else { memcpy(fftInData, data->data(), fftSizeInternal * sizeof(liquid_float_complex)); } } bool execute = false; if (num_written >= fftSizeInternal) { execute = true; memcpy(fftInput, fftInData, fftSizeInternal * sizeof(liquid_float_complex)); memcpy(fftLastData, fftInput, fftSizeInternal * sizeof(liquid_float_complex)); } else { if (lastDataSize + num_written < fftSizeInternal) { // priming unsigned int num_copy = fftSizeInternal - lastDataSize; if (num_written > num_copy) { num_copy = num_written; } memcpy(fftLastData, fftInData, num_copy * sizeof(liquid_float_complex)); lastDataSize += num_copy; } else { unsigned int num_last = (fftSizeInternal - num_written); memcpy(fftInput, fftLastData + (lastDataSize - num_last), num_last * sizeof(liquid_float_complex)); memcpy(fftInput + num_last, fftInData, num_written * sizeof(liquid_float_complex)); memcpy(fftLastData, fftInput, fftSizeInternal * sizeof(liquid_float_complex)); execute = true; } } if (execute) { SpectrumVisualData *output = outputBuffers.getBuffer(); if (output->spectrum_points.size() != fftSize * 2) { output->spectrum_points.resize(fftSize * 2); } if (doPeak) { if (output->spectrum_hold_points.size() != fftSize * 2) { output->spectrum_hold_points.resize(fftSize * 2); } } else { output->spectrum_hold_points.resize(0); } float fft_ceil = 0, fft_floor = 1; fft_execute(fftPlan); for (int i = 0, iMax = fftSizeInternal / 2; i < iMax; i++) { float a = fftOutput[i].real; float b = fftOutput[i].imag; float c = sqrt(a * a + b * b); float x = fftOutput[fftSizeInternal / 2 + i].real; float y = fftOutput[fftSizeInternal / 2 + i].imag; float z = sqrt(x * x + y * y); fft_result[i] = (z); fft_result[fftSizeInternal / 2 + i] = (c); } if (newResampler && lastView) { if (bwDiff < 0) { for (unsigned int i = 0, iMax = fftSizeInternal; i < iMax; i++) { fft_result_temp[i] = fft_result_ma[(fftSizeInternal/4) + (i/2)]; } for (unsigned int i = 0, iMax = fftSizeInternal; i < iMax; i++) { fft_result_ma[i] = fft_result_temp[i]; fft_result_temp[i] = fft_result_maa[(fftSizeInternal/4) + (i/2)]; } for (unsigned int i = 0, iMax = fftSizeInternal; i < iMax; i++) { fft_result_maa[i] = fft_result_temp[i]; } } else { for (size_t i = 0, iMax = fftSizeInternal; i < iMax; i++) { if (i < fftSizeInternal/4) { fft_result_temp[i] = 0; // fft_result_ma[fftSizeInternal/4]; } else if (i >= fftSizeInternal - fftSizeInternal/4) { fft_result_temp[i] = 0; // fft_result_ma[fftSizeInternal - fftSizeInternal/4-1]; } else { fft_result_temp[i] = fft_result_ma[(i-fftSizeInternal/4)*2]; } } for (unsigned int i = 0, iMax = fftSizeInternal; i < iMax; i++) { fft_result_ma[i] = fft_result_temp[i]; if (i < fftSizeInternal/4) { fft_result_temp[i] = 0; //fft_result_maa[fftSizeInternal/4]; } else if (i >= fftSizeInternal - fftSizeInternal/4) { fft_result_temp[i] = 0; // fft_result_maa[fftSizeInternal - fftSizeInternal/4-1]; } else { fft_result_temp[i] = fft_result_maa[(i-fftSizeInternal/4)*2]; } } for (unsigned int i = 0, iMax = fftSizeInternal; i < iMax; i++) { fft_result_maa[i] = fft_result_temp[i]; } } } for (int i = 0, iMax = fftSizeInternal; i < iMax; i++) { if (fft_result_maa[i] != fft_result_maa[i]) fft_result_maa[i] = fft_result[i]; fft_result_maa[i] += (fft_result_ma[i] - fft_result_maa[i]) * fft_average_rate; if (fft_result_ma[i] != fft_result_ma[i]) fft_result_ma[i] = fft_result[i]; fft_result_ma[i] += (fft_result[i] - fft_result_ma[i]) * fft_average_rate; if (fft_result_maa[i] > fft_ceil || fft_ceil != fft_ceil) { fft_ceil = fft_result_maa[i]; } if (fft_result_maa[i] < fft_floor || fft_floor != fft_floor) { fft_floor = fft_result_maa[i]; } if (doPeak) { if (fft_result_maa[i] > fft_result_peak[i]) { fft_result_peak[i] = fft_result_maa[i]; } } } if (fft_ceil_ma != fft_ceil_ma) fft_ceil_ma = fft_ceil; fft_ceil_ma = fft_ceil_ma + (fft_ceil - fft_ceil_ma) * 0.05; if (fft_ceil_maa != fft_ceil_maa) fft_ceil_maa = fft_ceil; fft_ceil_maa = fft_ceil_maa + (fft_ceil_ma - fft_ceil_maa) * 0.05; if (fft_floor_ma != fft_floor_ma) fft_floor_ma = fft_floor; fft_floor_ma = fft_floor_ma + (fft_floor - fft_floor_ma) * 0.05; if (fft_floor_maa != fft_floor_maa) fft_floor_maa = fft_floor; fft_floor_maa = fft_floor_maa + (fft_floor_ma - fft_floor_maa) * 0.05; if (doPeak) { if (fft_ceil_maa > fft_ceil_peak) { fft_ceil_peak = fft_ceil_maa; } if (fft_floor_maa < fft_floor_peak) { fft_floor_peak = fft_floor_maa; } } float sf = scaleFactor.load(); double visualRatio = (double(bandwidth) / double(resampleBw)); double visualStart = (double(fftSizeInternal) / 2.0) - (double(fftSizeInternal) * (visualRatio / 2.0)); double visualAccum = 0; double peak_acc = 0, acc = 0, accCount = 0, i = 0; double point_ceil = doPeak?fft_ceil_peak:fft_ceil_maa; double point_floor = doPeak?fft_floor_peak:fft_floor_maa; for (int x = 0, xMax = output->spectrum_points.size() / 2; x < xMax; x++) { visualAccum += visualRatio * double(SPECTRUM_VZM); while (visualAccum >= 1.0) { unsigned int idx = round(visualStart+i); if (idx > 0 && idx < fftSizeInternal) { acc += fft_result_maa[idx]; if (doPeak) { peak_acc += fft_result_peak[idx]; } } else { acc += fft_floor_maa; if (doPeak) { peak_acc += fft_floor_maa; } } accCount += 1.0; visualAccum -= 1.0; i++; } output->spectrum_points[x * 2] = ((float) x / (float) xMax); if (doPeak) { output->spectrum_hold_points[x * 2] = ((float) x / (float) xMax); } if (accCount) { output->spectrum_points[x * 2 + 1] = ((log10((acc/accCount)+0.25 - (point_floor-0.75)) / log10((point_ceil+0.25) - (point_floor-0.75))))*sf; acc = 0.0; if (doPeak) { output->spectrum_hold_points[x * 2 + 1] = ((log10((peak_acc/accCount)+0.25 - (point_floor-0.75)) / log10((point_ceil+0.25) - (point_floor-0.75))))*sf; peak_acc = 0.0; } accCount = 0.0; } } if (hideDC.load()) { // DC-spike removal long long freqMin = centerFreq-(bandwidth/2); long long freqMax = centerFreq+(bandwidth/2); long long zeroPt = (iqData->frequency-freqMin); if (freqMin < iqData->frequency && freqMax > iqData->frequency) { int freqRange = int(freqMax-freqMin); int freqStep = freqRange/fftSize; int fftStart = (zeroPt/freqStep)-(2000/freqStep); int fftEnd = (zeroPt/freqStep)+(2000/freqStep); // std::cout << "range:" << freqRange << ", step: " << freqStep << ", start: " << fftStart << ", end: " << fftEnd << std::endl; if (fftEnd-fftStart < 2) { fftEnd++; fftStart--; } int numSteps = (fftEnd-fftStart); int halfWay = fftStart+(numSteps/2); if ((fftEnd+numSteps/2+1 < (long long) fftSize) && (fftStart-numSteps/2-1 >= 0) && (fftEnd > fftStart)) { int n = 1; for (int i = fftStart; i < halfWay; i++) { output->spectrum_points[i * 2 + 1] = output->spectrum_points[(fftStart - n) * 2 + 1]; n++; } n = 1; for (int i = halfWay; i < fftEnd; i++) { output->spectrum_points[i * 2 + 1] = output->spectrum_points[(fftEnd + n) * 2 + 1]; n++; } if (doPeak) { int n = 1; for (int i = fftStart; i < halfWay; i++) { output->spectrum_hold_points[i * 2 + 1] = output->spectrum_hold_points[(fftStart - n) * 2 + 1]; n++; } n = 1; for (int i = halfWay; i < fftEnd; i++) { output->spectrum_hold_points[i * 2 + 1] = output->spectrum_hold_points[(fftEnd + n) * 2 + 1]; n++; } } } } } output->fft_ceiling = point_ceil/sf; output->fft_floor = point_floor; output->centerFreq = centerFreq; output->bandwidth = bandwidth; distribute(output); } } iqData->decRefCount(); lastView = is_view.load(); }
int main(int argc, char*argv[]) { // options float r=1.2f; // resampling rate (output/input) float As=80.0f; // resampling filter stop-band attenuation [dB] unsigned int nx=400; // number of input samples float fc=0.40f; // complex sinusoid frequency int dopt; while ((dopt = getopt(argc,argv,"hr:s:n:")) != EOF) { switch (dopt) { case 'h': usage(); return 0; case 'r': r = atof(optarg); break; case 's': As = atof(optarg); break; case 'n': nx = atoi(optarg); break; default: exit(1); } } // validate input if (nx == 0) { fprintf(stderr,"error: %s, number of input samples must be greater than zero\n", argv[0]); exit(1); } else if (r <= 0.0f) { fprintf(stderr,"error: %s, resampling rate must be greater than zero\n", argv[0]); exit(1); } else if ( fabsf(log2f(r)) > 10 ) { fprintf(stderr,"error: %s, resampling rate unreasonable\n", argv[0]); exit(1); } unsigned int i; // derived values unsigned int ny_alloc = (unsigned int) (2*(float)nx * r); // allocation for output // allocate memory for arrays float complex x[nx]; float complex y[ny_alloc]; // generate input unsigned int window_len = (3*nx)/4; for (i=0; i<nx; i++) x[i] = i < window_len ? cexpf(_Complex_I*2*M_PI*fc*i) * kaiser(i,window_len,10.0f,0.0f) : 0.0f; // create multi-stage arbitrary resampler object msresamp_crcf q = msresamp_crcf_create(r,As); msresamp_crcf_print(q); float delay = msresamp_crcf_get_delay(q); // run resampler unsigned int ny; msresamp_crcf_execute(q, x, nx, y, &ny); // print basic results printf("input samples : %u\n", nx); printf("output samples : %u\n", ny); printf("delay : %f samples\n", delay); // clean up allocated objects msresamp_crcf_destroy(q); // // export output // // open/initialize output file FILE*fid = fopen(OUTPUT_FILENAME,"w"); fprintf(fid,"%% %s: auto-generated file\n",OUTPUT_FILENAME); fprintf(fid,"clear all;\n"); fprintf(fid,"close all;\n"); fprintf(fid,"\n"); fprintf(fid,"r=%12.8f;\n", r); fprintf(fid,"delay = %f;\n", delay); // save input series fprintf(fid,"nx = %u;\n", nx); fprintf(fid,"x = zeros(1,nx);\n"); for (i=0; i<nx; i++) fprintf(fid,"x(%6u) = %12.4e + j*%12.4e;\n", i+1, crealf(x[i]), cimagf(x[i])); // save output series fprintf(fid,"ny = %u;\n", ny); fprintf(fid,"y = zeros(1,ny);\n"); for (i=0; i<ny; i++) fprintf(fid,"y(%6u) = %12.4e + j*%12.4e;\n", i+1, crealf(y[i]), cimagf(y[i])); // output results fprintf(fid,"\n\n"); fprintf(fid,"%% plot frequency-domain result\n"); fprintf(fid,"nfft=1024;\n"); fprintf(fid,"%% estimate PSD, normalize by array length\n"); fprintf(fid,"X=20*log10(abs(fftshift(fft(x,nfft)/length(x))));\n"); fprintf(fid,"Y=20*log10(abs(fftshift(fft(y,nfft)/length(y))));\n"); fprintf(fid,"G = max(X);\n"); fprintf(fid,"X = X - G;\n"); fprintf(fid,"Y = Y - G;\n"); fprintf(fid,"f=[0:(nfft-1)]/nfft-0.5;\n"); fprintf(fid,"figure;\n"); fprintf(fid,"if r>1, fx = f/r; fy = f; %% interpolated\n"); fprintf(fid,"else, fx = f; fy = f*r; %% decimated\n"); fprintf(fid,"end;\n"); fprintf(fid,"plot(fx,X,'Color',[0.5 0.5 0.5],fy,Y,'LineWidth',2);\n"); fprintf(fid,"grid on;\n\n"); fprintf(fid,"xlabel('normalized frequency');\n"); fprintf(fid,"ylabel('PSD [dB]');\n"); fprintf(fid,"legend('original','resampled','location','northeast');"); fprintf(fid,"axis([-0.5 0.5 -120 10]);\n"); fprintf(fid,"\n\n"); fprintf(fid,"%% plot time-domain result\n"); fprintf(fid,"tx=[0:(length(x)-1)];\n"); fprintf(fid,"ty=[0:(length(y)-1)]/r-delay;\n"); fprintf(fid,"figure;\n"); fprintf(fid,"subplot(2,1,1);\n"); fprintf(fid," plot(tx,real(x),'-s','Color',[0.5 0.5 0.5],'MarkerSize',1,...\n"); fprintf(fid," ty,real(y),'-s','Color',[0.5 0 0], 'MarkerSize',1);\n"); fprintf(fid," legend('original','resampled','location','northeast');"); fprintf(fid," xlabel('time');\n"); fprintf(fid," ylabel('real');\n"); fprintf(fid," grid on;\n\n"); fprintf(fid,"subplot(2,1,2);\n"); fprintf(fid," plot(tx,imag(x),'-s','Color',[0.5 0.5 0.5],'MarkerSize',1,...\n"); fprintf(fid," ty,imag(y),'-s','Color',[0 0.5 0], 'MarkerSize',1);\n"); fprintf(fid," legend('original','resampled','location','northeast');"); fprintf(fid," xlabel('time');\n"); fprintf(fid," ylabel('imag');\n"); fprintf(fid," grid on;\n\n"); fclose(fid); printf("results written to %s\n",OUTPUT_FILENAME); printf("done.\n"); return 0; }
int main() { float r=sqrtf(19); // resampling rate (output/input) unsigned int n=37; // number of input samples float As=60.0f; // stop-band attenuation [dB] unsigned int i; // derived values: number of input, output samples (adjusted for filter delay) unsigned int nx = 1.4*n; unsigned int ny_alloc = (unsigned int) (2*(float)nx * r); // allocation for output // allocate memory for arrays float complex x[nx]; float complex y[ny_alloc]; // generate input signal (filter pulse with frequency offset) float hf[n]; liquid_firdes_kaiser(n, 0.08f, 80.0f, 0, hf); for (i=0; i<nx; i++) x[i] = (i < n) ? hf[i]*cexpf(_Complex_I*1.17f*i) : 0.0f; // create resampler msresamp_crcf q = msresamp_crcf_create(r,As); float delay = msresamp_crcf_get_delay(q); // execute resampler, storing in output buffer unsigned int ny; msresamp_crcf_execute(q, x, nx, y, &ny); // print basic results printf("input samples : %u\n", nx); printf("output samples : %u\n", ny); printf("delay : %f samples\n", delay); // clean up allocated objects msresamp_crcf_destroy(q); // // export output files // FILE * fid; // // export time plot // fid = fopen(OUTPUT_FILENAME_TIME,"w"); fprintf(fid,"# %s: auto-generated file\n\n", OUTPUT_FILENAME_TIME); fprintf(fid,"reset\n"); fprintf(fid,"set terminal postscript eps enhanced color solid rounded\n"); //fprintf(fid,"set xrange [0:%u];\n",n); fprintf(fid,"set yrange [-1.2:1.2]\n"); fprintf(fid,"set size ratio 0.3\n"); fprintf(fid,"set xlabel 'Input Sample Index'\n"); fprintf(fid,"set key top right nobox\n"); fprintf(fid,"set ytics -5,1,5\n"); fprintf(fid,"set grid xtics ytics\n"); fprintf(fid,"set grid linetype 1 linecolor rgb '%s' lw 1\n", LIQUID_DOC_COLOR_GRID); fprintf(fid,"set multiplot layout 2,1 scale 1.0,1.0\n"); fprintf(fid,"# real\n"); fprintf(fid,"set ylabel 'Real'\n"); fprintf(fid,"plot '-' using 1:2 with linespoints pointtype 6 pointsize 0.9 linetype 1 linewidth 1 linecolor rgb '#666666' title 'original',\\\n"); fprintf(fid," '-' using 1:2 with points pointtype 7 pointsize 0.6 linecolor rgb '#008000' title 'resampled'\n"); // export input signal for (i=0; i<nx; i++) fprintf(fid,"%12.4e %12.4e %12.4e\n", (float)i, crealf(x[i]), cimagf(x[i])); fprintf(fid,"e\n"); // export output signal for (i=0; i<ny; i++) fprintf(fid,"%12.4e %12.4e %12.4e\n", (float)(i)/r - delay, crealf(y[i]), cimagf(y[i])); fprintf(fid,"e\n"); fprintf(fid,"# imag\n"); fprintf(fid,"set ylabel 'Imag'\n"); fprintf(fid,"plot '-' using 1:3 with linespoints pointtype 6 pointsize 0.9 linetype 1 linewidth 1 linecolor rgb '#666666' title 'original',\\\n"); fprintf(fid," '-' using 1:3 with points pointtype 7 pointsize 0.6 linecolor rgb '#800000' title 'resampled'\n"); // export input signal for (i=0; i<nx; i++) fprintf(fid,"%12.4e %12.4e %12.4e\n", (float)i, crealf(x[i]), cimagf(x[i])); fprintf(fid,"e\n"); // export output signal for (i=0; i<ny; i++) fprintf(fid,"%12.4e %12.4e %12.4e\n", (float)(i)/r - delay, crealf(y[i]), cimagf(y[i])); fprintf(fid,"e\n"); fprintf(fid,"unset multiplot\n"); // close output file fclose(fid); // // export spectrum plot // fid = fopen(OUTPUT_FILENAME_FREQ,"w"); unsigned int nfft = 512; float complex X[nfft]; float complex Y[nfft]; liquid_doc_compute_psdcf(x, nx, X, nfft, LIQUID_DOC_PSDWINDOW_NONE, 1); liquid_doc_compute_psdcf(y, ny, Y, nfft, LIQUID_DOC_PSDWINDOW_NONE, 1); fft_shift(X,nfft); fft_shift(Y,nfft); fprintf(fid,"# %s: auto-generated file\n\n", OUTPUT_FILENAME_FREQ); fprintf(fid,"reset\n"); fprintf(fid,"set terminal postscript eps enhanced color solid rounded\n"); fprintf(fid,"set xrange [-0.5:0.5];\n"); fprintf(fid,"set yrange [-120:20]\n"); fprintf(fid,"set size ratio 0.6\n"); fprintf(fid,"set xlabel 'Normalized Output Frequency'\n"); fprintf(fid,"set ylabel 'Power Spectral Density [dB]'\n"); fprintf(fid,"set key top right nobox\n"); fprintf(fid,"set grid xtics ytics\n"); fprintf(fid,"set pointsize 0.6\n"); fprintf(fid,"set grid linetype 1 linecolor rgb '%s' lw 1\n",LIQUID_DOC_COLOR_GRID); fprintf(fid,"# real\n"); fprintf(fid,"plot '-' using 1:2 with lines linetype 1 linewidth 4 linecolor rgb '#999999' title 'original',\\\n"); fprintf(fid," '-' using 1:2 with lines linetype 1 linewidth 4 linecolor rgb '#004080' title 'resampled'\n"); // export output for (i=0; i<nfft; i++) { float fx = ((float)(i) / (float)nfft - 0.5f) / r; fprintf(fid,"%12.8f %12.4e\n", fx, 20*log10f(cabsf(X[i]))); } fprintf(fid,"e\n"); for (i=0; i<nfft; i++) { float fy = ((float)(i) / (float)nfft - 0.5f); fprintf(fid,"%12.8f %12.4e\n", fy, 20*log10f(cabsf(Y[i]))); } fprintf(fid,"e\n"); fclose(fid); printf("done.\n"); return 0; }
void SpectrumVisualProcessor::process() { if (!isOutputEmpty()) { return; } if (!input || input->empty()) { return; } DemodulatorThreadIQData *iqData; input->pop(iqData); if (!iqData) { return; } iqData->busy_rw.lock(); busy_run.lock(); std::vector<liquid_float_complex> *data = &iqData->data; if (data && data->size()) { SpectrumVisualData *output = outputBuffers.getBuffer(); if (output->spectrum_points.size() < fftSize * 2) { output->spectrum_points.resize(fftSize * 2); } unsigned int num_written; if (is_view.load()) { if (!iqData->frequency || !iqData->sampleRate) { iqData->decRefCount(); iqData->busy_rw.unlock(); busy_run.unlock(); return; } resamplerRatio = (double) (bandwidth) / (double) iqData->sampleRate; int desired_input_size = fftSize / resamplerRatio; this->desiredInputSize.store(desired_input_size); if (iqData->data.size() < desired_input_size) { // std::cout << "fft underflow, desired: " << desired_input_size << " actual:" << input->data.size() << std::endl; desired_input_size = iqData->data.size(); } if (centerFreq != iqData->frequency) { if ((centerFreq - iqData->frequency) != shiftFrequency || lastInputBandwidth != iqData->sampleRate) { if (abs(iqData->frequency - centerFreq) < (wxGetApp().getSampleRate() / 2)) { shiftFrequency = centerFreq - iqData->frequency; nco_crcf_reset(freqShifter); nco_crcf_set_frequency(freqShifter, (2.0 * M_PI) * (((double) abs(shiftFrequency)) / ((double) iqData->sampleRate))); } } if (shiftBuffer.size() != desired_input_size) { if (shiftBuffer.capacity() < desired_input_size) { shiftBuffer.reserve(desired_input_size); } shiftBuffer.resize(desired_input_size); } if (shiftFrequency < 0) { nco_crcf_mix_block_up(freqShifter, &iqData->data[0], &shiftBuffer[0], desired_input_size); } else { nco_crcf_mix_block_down(freqShifter, &iqData->data[0], &shiftBuffer[0], desired_input_size); } } else { shiftBuffer.assign(iqData->data.begin(), iqData->data.end()); } if (!resampler || bandwidth != lastBandwidth || lastInputBandwidth != iqData->sampleRate) { float As = 60.0f; if (resampler) { msresamp_crcf_destroy(resampler); } resampler = msresamp_crcf_create(resamplerRatio, As); lastBandwidth = bandwidth; lastInputBandwidth = iqData->sampleRate; } int out_size = ceil((double) (desired_input_size) * resamplerRatio) + 512; if (resampleBuffer.size() != out_size) { if (resampleBuffer.capacity() < out_size) { resampleBuffer.reserve(out_size); } resampleBuffer.resize(out_size); } msresamp_crcf_execute(resampler, &shiftBuffer[0], desired_input_size, &resampleBuffer[0], &num_written); resampleBuffer.resize(fftSize); if (num_written < fftSize) { for (int i = 0; i < num_written; i++) { fftInData[i][0] = resampleBuffer[i].real; fftInData[i][1] = resampleBuffer[i].imag; } for (int i = num_written; i < fftSize; i++) { fftInData[i][0] = 0; fftInData[i][1] = 0; } } else { for (int i = 0; i < fftSize; i++) { fftInData[i][0] = resampleBuffer[i].real; fftInData[i][1] = resampleBuffer[i].imag; } } } else { num_written = data->size(); if (data->size() < fftSize) { for (int i = 0, iMax = data->size(); i < iMax; i++) { fftInData[i][0] = (*data)[i].real; fftInData[i][1] = (*data)[i].imag; } for (int i = data->size(); i < fftSize; i++) { fftInData[i][0] = 0; fftInData[i][1] = 0; } } else { for (int i = 0; i < fftSize; i++) { fftInData[i][0] = (*data)[i].real; fftInData[i][1] = (*data)[i].imag; } } } bool execute = false; if (num_written >= fftSize) { execute = true; memcpy(fftwInput, fftInData, fftSize * sizeof(fftwf_complex)); memcpy(fftLastData, fftwInput, fftSize * sizeof(fftwf_complex)); } else { if (lastDataSize + num_written < fftSize) { // priming unsigned int num_copy = fftSize - lastDataSize; if (num_written > num_copy) { num_copy = num_written; } memcpy(fftLastData, fftInData, num_copy * sizeof(fftwf_complex)); lastDataSize += num_copy; } else { unsigned int num_last = (fftSize - num_written); memcpy(fftwInput, fftLastData + (lastDataSize - num_last), num_last * sizeof(fftwf_complex)); memcpy(fftwInput + num_last, fftInData, num_written * sizeof(fftwf_complex)); memcpy(fftLastData, fftwInput, fftSize * sizeof(fftwf_complex)); execute = true; } } if (execute) { fftwf_execute(fftw_plan); float fft_ceil = 0, fft_floor = 1; if (fft_result.size() < fftSize) { fft_result.resize(fftSize); fft_result_ma.resize(fftSize); fft_result_maa.resize(fftSize); } for (int i = 0, iMax = fftSize / 2; i < iMax; i++) { float a = fftwOutput[i][0]; float b = fftwOutput[i][1]; float c = sqrt(a * a + b * b); float x = fftwOutput[fftSize / 2 + i][0]; float y = fftwOutput[fftSize / 2 + i][1]; float z = sqrt(x * x + y * y); fft_result[i] = (z); fft_result[fftSize / 2 + i] = (c); } for (int i = 0, iMax = fftSize; i < iMax; i++) { if (is_view.load()) { fft_result_maa[i] += (fft_result_ma[i] - fft_result_maa[i]) * fft_average_rate; fft_result_ma[i] += (fft_result[i] - fft_result_ma[i]) * fft_average_rate; } else { fft_result_maa[i] += (fft_result_ma[i] - fft_result_maa[i]) * fft_average_rate; fft_result_ma[i] += (fft_result[i] - fft_result_ma[i]) * fft_average_rate; } if (fft_result_maa[i] > fft_ceil) { fft_ceil = fft_result_maa[i]; } if (fft_result_maa[i] < fft_floor) { fft_floor = fft_result_maa[i]; } } fft_ceil_ma = fft_ceil_ma + (fft_ceil - fft_ceil_ma) * 0.05; fft_ceil_maa = fft_ceil_maa + (fft_ceil_ma - fft_ceil_maa) * 0.05; fft_floor_ma = fft_floor_ma + (fft_floor - fft_floor_ma) * 0.05; fft_floor_maa = fft_floor_maa + (fft_floor_ma - fft_floor_maa) * 0.05; float sf = scaleFactor.load(); for (int i = 0, iMax = fftSize; i < iMax; i++) { float v = (log10(fft_result_maa[i]+0.25 - (fft_floor_maa-0.75)) / log10((fft_ceil_maa+0.25) - (fft_floor_maa-0.75))); output->spectrum_points[i * 2] = ((float) i / (float) iMax); output->spectrum_points[i * 2 + 1] = v*sf; } if (hideDC.load()) { // DC-spike removal long long freqMin = centerFreq-(bandwidth/2); long long freqMax = centerFreq+(bandwidth/2); long long zeroPt = (iqData->frequency-freqMin); if (freqMin < iqData->frequency && freqMax > iqData->frequency) { int freqRange = int(freqMax-freqMin); int freqStep = freqRange/fftSize; int fftStart = (zeroPt/freqStep)-(2000/freqStep); int fftEnd = (zeroPt/freqStep)+(2000/freqStep); // std::cout << "range:" << freqRange << ", step: " << freqStep << ", start: " << fftStart << ", end: " << fftEnd << std::endl; if (fftEnd-fftStart < 2) { fftEnd++; fftStart--; } int numSteps = (fftEnd-fftStart); int halfWay = fftStart+(numSteps/2); if ((fftEnd+numSteps/2+1 < fftSize) && (fftStart-numSteps/2-1 >= 0) && (fftEnd > fftStart)) { int n = 1; for (int i = fftStart; i < halfWay; i++) { output->spectrum_points[i * 2 + 1] = output->spectrum_points[(fftStart - n) * 2 + 1]; n++; } n = 1; for (int i = halfWay; i < fftEnd; i++) { output->spectrum_points[i * 2 + 1] = output->spectrum_points[(fftEnd + n) * 2 + 1]; n++; } } } } output->fft_ceiling = fft_ceil_maa/sf; output->fft_floor = fft_floor_maa; } distribute(output); } iqData->decRefCount(); iqData->busy_rw.unlock(); busy_run.unlock(); }
// main program int main (int argc, char **argv) { // command-line options int verbose = 1; int ppm_error = 0; int gain = 0; float rx_resamp_rate; float bandwidth = 800e3f; int r, n_read; uint32_t frequency = 100000000; uint32_t samp_rate = DEFAULT_SAMPLE_RATE; uint32_t out_block_size = DEFAULT_BUF_LENGTH; uint8_t *buffer; complex float *buffer_norm; int dev_index = 0; int dev_given = 0; struct sigaction sigact; normalizer_t *norm; float kf = 0.1f; // modulation factor liquid_freqdem_type type = LIQUID_FREQDEM_DELAYCONJ; // int d; while ((d = getopt(argc,argv,"hf:b:B:G:p:s:")) != EOF) { switch (d) { case 'h': usage(); return 0; case 'f': frequency = atof(optarg); break; case 'b': bandwidth = atof(optarg); break; case 'B': out_block_size = (uint32_t)atof(optarg); break; case 'G': gain = (int)(atof(optarg) * 10); break; case 'p': ppm_error = atoi(optarg); break; case 's': samp_rate = (uint32_t)atofs(optarg); break; case 'd': dev_index = verbose_device_search(optarg); dev_given = 1; break; default: usage(); return 1; } } if (!dev_given) { dev_index = verbose_device_search("0"); } if (dev_index < 0) { exit(1); } r = rtlsdr_open(&dev, (uint32_t)dev_index); if (r < 0) { fprintf(stderr, "Failed to open rtlsdr device #%d.\n", dev_index); exit(1); } sigact.sa_handler = sighandler; sigemptyset(&sigact.sa_mask); sigact.sa_flags = 0; sigaction(SIGINT, &sigact, NULL); sigaction(SIGTERM, &sigact, NULL); sigaction(SIGQUIT, &sigact, NULL); sigaction(SIGPIPE, &sigact, NULL); /* Set the sample rate */ verbose_set_sample_rate(dev, samp_rate); /* Set the frequency */ verbose_set_frequency(dev, frequency); if (0 == gain) { /* Enable automatic gain */ verbose_auto_gain(dev); } else { /* Enable manual gain */ gain = nearest_gain(dev, gain); verbose_gain_set(dev, gain); } verbose_ppm_set(dev, ppm_error); rx_resamp_rate = bandwidth/samp_rate; printf("frequency : %10.4f [MHz]\n", frequency*1e-6f); printf("bandwidth : %10.4f [kHz]\n", bandwidth*1e-3f); printf("sample rate : %10.4f kHz = %10.4f kHz * %8.6f\n", samp_rate * 1e-3f, bandwidth * 1e-3f, 1.0f / rx_resamp_rate); printf("verbosity : %s\n", (verbose?"enabled":"disabled")); unsigned int i,j; // add arbitrary resampling component msresamp_crcf resamp = msresamp_crcf_create(rx_resamp_rate, 60.0f); assert(resamp); //allocate recv buffer buffer = malloc(out_block_size * sizeof(uint8_t)); assert(buffer); buffer_norm = malloc(out_block_size * sizeof(complex float)); assert(buffer_norm); // create buffer for arbitrary resamper output int b_len = ((int)(out_block_size * rx_resamp_rate) + 64) >> 1; complex float buffer_resamp[b_len]; int16_t buffer_demod[b_len]; debug("resamp_buffer_len: %d\n", b_len); norm = normalizer_create(); verbose_reset_buffer(dev); freqdem dem = freqdem_create(kf,type); while (!do_exit) { // grab data from device r = rtlsdr_read_sync(dev, buffer, out_block_size, &n_read); if (r < 0) { fprintf(stderr, "WARNING: sync read failed.\n"); break; } if ((bytes_to_read > 0) && (bytes_to_read < (uint32_t)n_read)) { n_read = bytes_to_read; do_exit = 1; } // push data through arbitrary resampler and give to frame synchronizer // TODO : apply bandwidth-dependent gain for (i=0; i<n_read/2; i++) { // grab sample from usrp buffer buffer_norm[i] = normalizer_normalize(norm, *((uint16_t*)buffer+i)); } // push through resampler (one at a time) unsigned int nw; float demod; msresamp_crcf_execute(resamp, buffer_norm, n_read/2, buffer_resamp, &nw); for(j=0;j<nw;j++) { freqdem_demodulate(dem, buffer_resamp[j], &demod); buffer_demod[j] = to_int16(demod); } if (fwrite(buffer_demod, 2, nw, stdout) != (size_t)nw) { fprintf(stderr, "Short write, samples lost, exiting!\n"); break; } if ((uint32_t)n_read < out_block_size) { fprintf(stderr, "Short read, samples lost, exiting!\n"); break; } if (bytes_to_read > 0) bytes_to_read -= n_read; } // destroy objects freqdem_destroy(dem); normalizer_destroy(&norm); msresamp_crcf_destroy(resamp); rtlsdr_close(dev); free (buffer); return 0; }
// main program int main (int argc, char **argv) { // command-line options int verbose = 1; int ppm_error = 0; int gain = 0; unsigned int nfft = 64; float offset = -65.0f; float scale = 5.0f; float fft_rate = 10.0f; float rx_resamp_rate; float bandwidth = 800e3f; unsigned int logsize = 4096; char filename[256] = "rtl_asgram.dat"; int r, n_read; uint32_t frequency = 100000000; uint32_t samp_rate = DEFAULT_SAMPLE_RATE; uint32_t out_block_size = DEFAULT_BUF_LENGTH; uint8_t *buffer; int dev_index = 0; int dev_given = 0; struct sigaction sigact; normalizer_t *norm; // int d; while ((d = getopt(argc,argv,"hf:b:B:G:n:p:s:o:r:L:F:")) != EOF) { switch (d) { case 'h': usage(); return 0; case 'f': frequency = atof(optarg); break; case 'b': bandwidth = atof(optarg); break; case 'B': out_block_size = (uint32_t)atof(optarg); break; case 'G': gain = (int)(atof(optarg) * 10); break; case 'n': nfft = atoi(optarg); break; case 'o': offset = atof(optarg); break; case 'p': ppm_error = atoi(optarg); break; case 's': samp_rate = (uint32_t)atofs(optarg); break; case 'r': fft_rate = atof(optarg); break; case 'L': logsize = atoi(optarg); break; case 'F': strncpy(filename,optarg,255); break; case 'd': dev_index = verbose_device_search(optarg); dev_given = 1; break; default: usage(); return 1; } } // validate parameters if (fft_rate <= 0.0f || fft_rate > 100.0f) { fprintf(stderr,"error: %s, fft rate must be in (0, 100) Hz\n", argv[0]); exit(1); } if (!dev_given) { dev_index = verbose_device_search("0"); } if (dev_index < 0) { exit(1); } r = rtlsdr_open(&dev, (uint32_t)dev_index); if (r < 0) { fprintf(stderr, "Failed to open rtlsdr device #%d.\n", dev_index); exit(1); } sigact.sa_handler = sighandler; sigemptyset(&sigact.sa_mask); sigact.sa_flags = 0; sigaction(SIGINT, &sigact, NULL); sigaction(SIGTERM, &sigact, NULL); sigaction(SIGQUIT, &sigact, NULL); sigaction(SIGPIPE, &sigact, NULL); /* Set the sample rate */ verbose_set_sample_rate(dev, samp_rate); /* Set the frequency */ verbose_set_frequency(dev, frequency); if (0 == gain) { /* Enable automatic gain */ verbose_auto_gain(dev); } else { /* Enable manual gain */ gain = nearest_gain(dev, gain); verbose_gain_set(dev, gain); } verbose_ppm_set(dev, ppm_error); rx_resamp_rate = bandwidth/samp_rate; printf("frequency : %10.4f [MHz]\n", frequency*1e-6f); printf("bandwidth : %10.4f [kHz]\n", bandwidth*1e-3f); printf("sample rate : %10.4f kHz = %10.4f kHz * %8.6f\n", samp_rate * 1e-3f, bandwidth * 1e-3f, 1.0f / rx_resamp_rate); printf("verbosity : %s\n", (verbose?"enabled":"disabled")); unsigned int i; // add arbitrary resampling component msresamp_crcf resamp = msresamp_crcf_create(rx_resamp_rate, 60.0f); assert(resamp); // create buffer for sample logging windowcf log = windowcf_create(logsize); // create ASCII spectrogram object float maxval; float maxfreq; char ascii[nfft+1]; ascii[nfft] = '\0'; // append null character to end of string asgram q = asgram_create(nfft); asgram_set_scale(q, offset, scale); // assemble footer unsigned int footer_len = nfft + 16; char footer[footer_len+1]; for (i=0; i<footer_len; i++) footer[i] = ' '; footer[1] = '['; footer[nfft/2 + 3] = '+'; footer[nfft + 4] = ']'; sprintf(&footer[nfft+6], "%8.3f MHz", frequency*1e-6f); unsigned int msdelay = 1000 / fft_rate; // create/initialize Hamming window float w[nfft]; for (i=0; i<nfft; i++) w[i] = hamming(i,nfft); //allocate recv buffer buffer = malloc(out_block_size * sizeof(uint8_t)); assert(buffer); // create buffer for arbitrary resamper output int b_len = ((int)(out_block_size * rx_resamp_rate) + 64) >> 1; complex float buffer_resamp[b_len]; debug("resamp_buffer_len: %d", b_len); // timer to control asgram output timer t1 = timer_create(); timer_tic(t1); norm = normalizer_create(); verbose_reset_buffer(dev); while (!do_exit) { // grab data from device r = rtlsdr_read_sync(dev, buffer, out_block_size, &n_read); if (r < 0) { fprintf(stderr, "WARNING: sync read failed.\n"); break; } if ((bytes_to_read > 0) && (bytes_to_read < (uint32_t)n_read)) { n_read = bytes_to_read; do_exit = 1; } // push data through arbitrary resampler and give to frame synchronizer // TODO : apply bandwidth-dependent gain for (i=0; i<n_read/2; i++) { // grab sample from usrp buffer complex float rtlsdr_sample = normalizer_normalize(norm, *((uint16_t*)buffer+i)); // push through resampler (one at a time) unsigned int nw; msresamp_crcf_execute(resamp, &rtlsdr_sample, 1, buffer_resamp, &nw); // push resulting samples into asgram object asgram_push(q, buffer_resamp, nw); // write samples to log windowcf_write(log, buffer_resamp, nw); } if ((uint32_t)n_read < out_block_size) { fprintf(stderr, "Short read, samples lost, exiting!\n"); break; } if (bytes_to_read > 0) bytes_to_read -= n_read; if (timer_toc(t1) > msdelay*1e-3f) { // reset timer timer_tic(t1); // run the spectrogram asgram_execute(q, ascii, &maxval, &maxfreq); // print the spectrogram printf(" > %s < pk%5.1fdB [%5.2f]\n", ascii, maxval, maxfreq); printf("%s\r", footer); fflush(stdout); } } // try to write samples to file FILE * fid = fopen(filename,"w"); if (fid != NULL) { // write header fprintf(fid, "# %s : auto-generated file\n", filename); fprintf(fid, "#\n"); fprintf(fid, "# num_samples : %u\n", logsize); fprintf(fid, "# frequency : %12.8f MHz\n", frequency*1e-6f); fprintf(fid, "# bandwidth : %12.8f kHz\n", bandwidth*1e-3f); // save results to file complex float * rc; // read pointer windowcf_read(log, &rc); for (i=0; i<logsize; i++) fprintf(fid, "%12.4e %12.4e\n", crealf(rc[i]), cimagf(rc[i])); // close it up fclose(fid); printf("results written to '%s'\n", filename); } else { fprintf(stderr,"error: %s, could not open '%s' for writing\n", argv[0], filename); } // destroy objects normalizer_destroy(&norm); msresamp_crcf_destroy(resamp); windowcf_destroy(log); asgram_destroy(q); timer_destroy(t1); rtlsdr_close(dev); free (buffer); return 0; }
int main(int argc, char*argv[]) { // options float r=0.23175f; // resampling rate (output/input) float As=60.0f; // resampling filter stop-band attenuation [dB] unsigned int n=400; // number of input samples float fc=0.017f; // complex sinusoid frequency int dopt; while ((dopt = getopt(argc,argv,"hr:s:n:f:")) != EOF) { switch (dopt) { case 'h': usage(); return 0; case 'r': r = atof(optarg); break; case 's': As = atof(optarg); break; case 'n': n = atoi(optarg); break; case 'f': fc = atof(optarg); break; default: exit(1); } } // validate input if (n == 0) { fprintf(stderr,"error: %s, number of input samples must be greater than zero\n", argv[0]); exit(1); } else if (r <= 0.0f) { fprintf(stderr,"error: %s, resampling rate must be greater than zero\n", argv[0]); exit(1); } else if ( fabsf(log2f(r)) > 10 ) { fprintf(stderr,"error: %s, resampling rate unreasonable\n", argv[0]); exit(1); } unsigned int i; // create multi-stage arbitrary resampler object msresamp_crcf q = msresamp_crcf_create(r,As); msresamp_crcf_print(q); float delay = msresamp_crcf_get_delay(q); // number of input samples (zero-padded) unsigned int nx = n + (int)ceilf(delay) + 10; // output buffer with extra padding for good measure unsigned int ny_alloc = (unsigned int) (2*(float)nx * r); // allocation for output // allocate memory for arrays float complex x[nx]; float complex y[ny_alloc]; // generate input signal float wsum = 0.0f; for (i=0; i<nx; i++) { // compute window float w = i < n ? kaiser(i, n, 10.0f, 0.0f) : 0.0f; // apply window to complex sinusoid x[i] = cexpf(_Complex_I*2*M_PI*fc*i) * w; // accumulate window wsum += w; } // run resampler unsigned int ny; msresamp_crcf_execute(q, x, nx, y, &ny); // clean up allocated objects msresamp_crcf_destroy(q); // // analyze resulting signal // // check that the actual resampling rate is close to the target float r_actual = (float)ny / (float)nx; float fy = fc / r; // expected output frequency // run FFT and ensure that carrier has moved and that image // frequencies and distortion have been adequately suppressed unsigned int nfft = 1 << liquid_nextpow2(ny); float complex yfft[nfft]; // fft input float complex Yfft[nfft]; // fft output for (i=0; i<nfft; i++) yfft[i] = i < ny ? y[i] : 0.0f; fft_run(nfft, yfft, Yfft, LIQUID_FFT_FORWARD, 0); fft_shift(Yfft, nfft); // run FFT shift // find peak frequency float Ypeak = 0.0f; float fpeak = 0.0f; float max_sidelobe = -1e9f; // maximum side-lobe [dB] float main_lobe_width = 0.07f; // TODO: figure this out from Kaiser's equations for (i=0; i<nfft; i++) { // normalized output frequency float f = (float)i/(float)nfft - 0.5f; // scale FFT output appropriately float Ymag = 20*log10f( cabsf(Yfft[i] / (r * wsum)) ); // find frequency location of maximum magnitude if (Ymag > Ypeak || i==0) { Ypeak = Ymag; fpeak = f; } // find peak side-lobe value, ignoring frequencies // within a certain range of signal frequency if ( fabsf(f-fy) > main_lobe_width ) max_sidelobe = Ymag > max_sidelobe ? Ymag : max_sidelobe; } // print results and check frequency location printf("output results:\n"); printf(" output delay : %12.8f samples\n", delay); printf(" desired resampling rate : %12.8f\n", r); printf(" measured resampling rate : %12.8f (%u/%u)\n", r_actual, ny, nx); printf(" peak spectrum : %12.8f dB (expected 0.0 dB)\n", Ypeak); printf(" peak frequency : %12.8f (expected %-12.8f)\n", fpeak, fy); printf(" max sidelobe : %12.8f dB (expected at least %.2f dB)\n", max_sidelobe, -As); // // export results // FILE * fid = fopen(OUTPUT_FILENAME,"w"); fprintf(fid,"%% %s: auto-generated file\n",OUTPUT_FILENAME); fprintf(fid,"clear all;\n"); fprintf(fid,"close all;\n"); fprintf(fid,"delay=%f;\n", delay); fprintf(fid,"r=%12.8f;\n", r); fprintf(fid,"nx = %u;\n", nx); fprintf(fid,"x = zeros(1,nx);\n"); for (i=0; i<nx; i++) fprintf(fid,"x(%3u) = %12.4e + j*%12.4e;\n", i+1, crealf(x[i]), cimagf(x[i])); fprintf(fid,"ny = %u;\n", ny); fprintf(fid,"y = zeros(1,ny);\n"); for (i=0; i<ny; i++) fprintf(fid,"y(%3u) = %12.4e + j*%12.4e;\n", i+1, crealf(y[i]), cimagf(y[i])); // time-domain results fprintf(fid,"\n"); fprintf(fid,"%% plot time-domain result\n"); fprintf(fid,"tx=[0:(length(x)-1)];\n"); fprintf(fid,"ty=[0:(length(y)-1)]/r-delay;\n"); fprintf(fid,"tmin = min(tx(1), ty(1) );\n"); fprintf(fid,"tmax = max(tx(end),ty(end));\n"); fprintf(fid,"figure;\n"); fprintf(fid,"subplot(2,1,1);\n"); fprintf(fid," plot(tx,real(x),'-s','Color',[0.5 0.5 0.5],'MarkerSize',1,...\n"); fprintf(fid," ty,real(y),'-s','Color',[0.5 0 0], 'MarkerSize',1);\n"); fprintf(fid," legend('original','resampled','location','northeast');"); fprintf(fid," axis([tmin tmax -1.2 1.2]);\n"); fprintf(fid," grid on;\n"); fprintf(fid," xlabel('time');\n"); fprintf(fid," ylabel('real');\n"); fprintf(fid,"subplot(2,1,2);\n"); fprintf(fid," plot(tx,imag(x),'-s','Color',[0.5 0.5 0.5],'MarkerSize',1,...\n"); fprintf(fid," ty,imag(y),'-s','Color',[0 0.5 0], 'MarkerSize',1);\n"); fprintf(fid," legend('original','resampled','location','northeast');"); fprintf(fid," axis([tmin tmax -1.2 1.2]);\n"); fprintf(fid," grid on;\n"); fprintf(fid," xlabel('time');\n"); fprintf(fid," ylabel('imag');\n"); // frequency-domain results fprintf(fid,"\n\n"); fprintf(fid,"%% plot frequency-domain result\n"); fprintf(fid,"nfft=2^nextpow2(max(nx,ny));\n"); fprintf(fid,"%% estimate PSD, normalize by array length\n"); fprintf(fid,"X=20*log10(abs(fftshift(fft(x,nfft)/length(x))));\n"); fprintf(fid,"Y=20*log10(abs(fftshift(fft(y,nfft)/length(y))));\n"); fprintf(fid,"G=max(X);\n"); fprintf(fid,"X=X-G;\n"); fprintf(fid,"Y=Y-G;\n"); fprintf(fid,"f=[0:(nfft-1)]/nfft-0.5;\n"); fprintf(fid,"figure;\n"); fprintf(fid,"if r>1, fx = f/r; fy = f; %% interpolated\n"); fprintf(fid,"else, fx = f; fy = f*r; %% decimated\n"); fprintf(fid,"end;\n"); fprintf(fid,"plot(fx,X,'LineWidth',1, 'Color',[0.5 0.5 0.5],...\n"); fprintf(fid," fy,Y,'LineWidth',1.5,'Color',[0.1 0.3 0.5]);\n"); fprintf(fid,"grid on;\n"); fprintf(fid,"xlabel('normalized frequency');\n"); fprintf(fid,"ylabel('PSD [dB]');\n"); fprintf(fid,"legend('original','resampled','location','northeast');"); fprintf(fid,"axis([-0.5 0.5 -120 20]);\n"); fclose(fid); printf("results written to %s\n",OUTPUT_FILENAME); printf("done.\n"); return 0; }