-
Notifications
You must be signed in to change notification settings - Fork 186
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
xtrx read firmware data, extract common lms7002m configuration functions
- Loading branch information
Showing
30 changed files
with
885 additions
and
252 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,203 @@ | ||
#include "FFT.h" | ||
|
||
#include <assert.h> | ||
#include <chrono> | ||
|
||
namespace lime { | ||
|
||
FFT::FFT(uint32_t size) | ||
: samplesFIFO(size * 128) | ||
{ | ||
m_fftCalcIn.resize(size); | ||
m_fftCalcOut.resize(size); | ||
m_fftCalcPlan = kiss_fft_alloc(size, 0, 0, 0); | ||
|
||
doWork.store(true, std::memory_order_relaxed); | ||
mWorkerThread = std::thread(&FFT::ProcessLoop, this); | ||
|
||
GenerateWindowCoefficients(WindowFunctionType::BLACKMAN_HARRIS, m_fftCalcIn.size(), mWindowCoefs); | ||
} | ||
|
||
FFT::~FFT() | ||
{ | ||
doWork.store(false, std::memory_order_relaxed); | ||
inputAvailable.notify_one(); | ||
if (mWorkerThread.joinable()) | ||
mWorkerThread.join(); | ||
kiss_fft_free(m_fftCalcPlan); | ||
} | ||
|
||
int FFT::PushSamples(const complex32f_t* samples, uint32_t count) | ||
{ | ||
int produced = samplesFIFO.Produce(samples, count); | ||
inputAvailable.notify_one(); | ||
return produced; | ||
} | ||
|
||
void FFT::SetResultsCallback(FFT::CallbackType fptr, void* userData) | ||
{ | ||
resultsCallback = fptr; | ||
mUserData = userData; | ||
} | ||
|
||
std::vector<float> FFT::Calc(const std::vector<complex32f_t>& samples, WindowFunctionType window) | ||
{ | ||
const int fftSize = samples.size(); | ||
std::vector<float> coefs; | ||
GenerateWindowCoefficients(window, fftSize, coefs); | ||
|
||
std::vector<kiss_fft_cpx> fftIn(fftSize); | ||
std::vector<kiss_fft_cpx> fftOut(fftSize); | ||
std::vector<float> bins(fftSize); | ||
kiss_fft_cfg plan = kiss_fft_alloc(fftSize, 0, 0, 0); | ||
|
||
for (int i = 0; i < fftSize; ++i) | ||
{ | ||
fftIn[i].r = samples[i].i * coefs[i]; | ||
fftIn[i].i = samples[i].q * coefs[i]; | ||
} | ||
kiss_fft(plan, fftIn.data(), fftOut.data()); | ||
for (int i = 0; i < fftSize; ++i) | ||
{ | ||
bins[i] = (fftOut[i].r * fftOut[i].r + fftOut[i].i * fftOut[i].i) / (fftSize * fftSize); | ||
} | ||
kiss_fft_free(plan); | ||
return bins; | ||
} | ||
|
||
void FFT::ConvertToDBFS(std::vector<float>& bins) | ||
{ | ||
for (float& amplitude : bins) | ||
amplitude = amplitude > 0 ? 10 * log10(amplitude) : -150; | ||
} | ||
|
||
void FFT::Calculate(const complex16_t* src, uint32_t count, std::vector<float>& outputBins) | ||
{ | ||
assert(count == m_fftCalcIn.size()); | ||
for (uint32_t i = 0; i < count; ++i) | ||
{ | ||
m_fftCalcIn[i].r = src[i].i / 32768.0 * mWindowCoefs[i]; | ||
m_fftCalcIn[i].i = src[i].q / 32768.0 * mWindowCoefs[i]; | ||
} | ||
kiss_fft(m_fftCalcPlan, m_fftCalcIn.data(), m_fftCalcOut.data()); | ||
outputBins.resize(count); | ||
for (uint32_t i = 0; i < count; ++i) | ||
{ | ||
float amplitude = (m_fftCalcOut[i].r * m_fftCalcOut[i].r + m_fftCalcOut[i].i * m_fftCalcOut[i].i) / (count * count); | ||
outputBins[i] = amplitude > 0 ? 10 * log10(amplitude) : -150; | ||
} | ||
} | ||
|
||
void FFT::Calculate(const complex32f_t* src, uint32_t count, std::vector<float>& outputBins) | ||
{ | ||
assert(count == m_fftCalcIn.size()); | ||
for (uint32_t i = 0; i < count; ++i) | ||
{ | ||
m_fftCalcIn[i].r = src[i].i * mWindowCoefs[i]; | ||
m_fftCalcIn[i].i = src[i].q * mWindowCoefs[i]; | ||
} | ||
kiss_fft(m_fftCalcPlan, m_fftCalcIn.data(), m_fftCalcOut.data()); | ||
outputBins.resize(count); | ||
for (uint32_t i = 0; i < count; ++i) | ||
{ | ||
float amplitude = (m_fftCalcOut[i].r * m_fftCalcOut[i].r + m_fftCalcOut[i].i * m_fftCalcOut[i].i) / (count * count); | ||
outputBins[i] = amplitude > 0 ? 10 * log10(amplitude) : -150; | ||
} | ||
} | ||
|
||
void FFT::ProcessLoop() | ||
{ | ||
std::vector<complex32f_t> samples(m_fftCalcIn.size()); | ||
std::vector<float> fftBins(m_fftCalcOut.size()); | ||
std::vector<float> avgOutput(m_fftCalcOut.size()); | ||
uint32_t samplesReady = 0; | ||
|
||
std::unique_lock<std::mutex> lk(inputMutex); | ||
|
||
int resultsDone = 0; | ||
while (doWork.load(std::memory_order_relaxed) == true) | ||
{ | ||
if (inputAvailable.wait_for(lk, std::chrono::milliseconds(2000)) == std::cv_status::timeout) | ||
{ | ||
printf("plot timeout\n"); | ||
continue; | ||
} | ||
|
||
int samplesNeeded = samples.size() - samplesReady; | ||
int ret = samplesFIFO.Consume(samples.data() + samplesReady, samplesNeeded); | ||
samplesReady += ret; | ||
|
||
if (samplesReady == samples.size()) | ||
{ | ||
// auto t1 = std::chrono::high_resolution_clock::now(); | ||
Calculate(samples.data(), samples.size(), fftBins); | ||
++resultsDone; | ||
samplesReady = 0; | ||
|
||
for (uint32_t i = 0; i < fftBins.size(); ++i) | ||
avgOutput[i] += fftBins[i] * fftBins[i]; | ||
|
||
if (resultsDone == avgCount) | ||
{ | ||
if (resultsCallback) | ||
{ | ||
// to log scale | ||
for (uint i = 0; i < fftBins.size(); ++i) | ||
avgOutput[i] = sqrt(avgOutput[i] / avgCount); | ||
for (uint i = 0; i < fftBins.size(); ++i) | ||
{ | ||
fftBins[i] = avgOutput[i] > 0 ? 10 * log10(avgOutput[i]) : -150; | ||
} | ||
resultsCallback(fftBins, mUserData); | ||
} | ||
resultsDone = 0; | ||
memset(avgOutput.data(), 0, avgOutput.size() * sizeof(float)); | ||
} | ||
// auto t2 = std::chrono::high_resolution_clock::now(); | ||
// auto timePeriod = std::chrono::duration_cast<std::chrono::microseconds>(t2 - t1).count(); | ||
//printf("FFT update %lius\n", timePeriod); | ||
} | ||
} | ||
} | ||
|
||
void FFT::GenerateWindowCoefficients(WindowFunctionType func, uint32_t N /*coef count*/, std::vector<float>& windowFcoefs) | ||
{ | ||
float amplitudeCorrection = 1; | ||
windowFcoefs.resize(N); | ||
float a0 = 0.35875; | ||
float a1 = 0.48829; | ||
float a2 = 0.14128; | ||
float a3 = 0.01168; | ||
float PI = 3.14159265359; | ||
switch (func) | ||
{ | ||
case WindowFunctionType::BLACKMAN_HARRIS: | ||
for (uint32_t i = 0; i < N; ++i) | ||
windowFcoefs[i] = | ||
a0 - a1 * cos((2 * PI * i) / (N - 1)) + a2 * cos((4 * PI * i) / (N - 1)) - a3 * cos((6 * PI * i) / (N - 1)); | ||
break; | ||
case WindowFunctionType::HAMMING: | ||
amplitudeCorrection = 0; | ||
a0 = 0.54; | ||
for (uint32_t i = 0; i < N; ++i) | ||
windowFcoefs[i] = a0 - (1 - a0) * cos((2 * PI * i) / (N)); | ||
break; | ||
case WindowFunctionType::HANNING: | ||
amplitudeCorrection = 0; | ||
for (uint32_t i = 0; i < N; ++i) | ||
windowFcoefs[i] = 0.5 * (1 - cos((2 * PI * i) / (N))); | ||
break; | ||
case WindowFunctionType::NONE: | ||
default: | ||
for (uint32_t i = 0; i < N; ++i) | ||
windowFcoefs[i] = 1; | ||
return; | ||
} | ||
for (uint32_t i = 0; i < N; ++i) | ||
amplitudeCorrection += windowFcoefs[i]; | ||
amplitudeCorrection = 1.0 / (amplitudeCorrection / N); | ||
for (uint32_t i = 0; i < N; ++i) | ||
windowFcoefs[i] *= amplitudeCorrection; | ||
} | ||
|
||
} // namespace lime |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
#pragma once | ||
|
||
#include "limesuite/complex.h" | ||
#include <atomic> | ||
#include <thread> | ||
#include <mutex> | ||
#include <condition_variable> | ||
#include "RingBuffer.h" | ||
#include "../external/kissFFT/kiss_fft.h" | ||
|
||
namespace lime { | ||
|
||
class FFT | ||
{ | ||
public: | ||
typedef void (*CallbackType)(const std::vector<float>& bins, void* userData); | ||
FFT(uint32_t size); | ||
~FFT(); | ||
|
||
int PushSamples(const complex32f_t* samples, uint32_t count); | ||
void SetResultsCallback(FFT::CallbackType fptr, void* userData); | ||
|
||
enum class WindowFunctionType { NONE = 0, BLACKMAN_HARRIS, HAMMING, HANNING }; | ||
static void GenerateWindowCoefficients(WindowFunctionType type, uint32_t coefCount, std::vector<float>& coefs); | ||
static std::vector<float> Calc(const std::vector<complex32f_t>& samples, WindowFunctionType window = WindowFunctionType::NONE); | ||
static void ConvertToDBFS(std::vector<float>& bins); | ||
|
||
private: | ||
void Calculate(const complex16_t* src, uint32_t count, std::vector<float>& outputBins); | ||
void Calculate(const complex32f_t* src, uint32_t count, std::vector<float>& outputBins); | ||
void ProcessLoop(); | ||
|
||
RingBuffer<complex32f_t> samplesFIFO; | ||
|
||
std::thread mWorkerThread; | ||
|
||
kiss_fft_cfg m_fftCalcPlan; | ||
std::vector<float> mWindowCoefs; | ||
std::vector<kiss_fft_cpx> m_fftCalcIn; | ||
std::vector<kiss_fft_cpx> m_fftCalcOut; | ||
|
||
std::atomic<bool> doWork; | ||
std::condition_variable inputAvailable; | ||
std::mutex inputMutex; | ||
|
||
CallbackType resultsCallback; | ||
void* mUserData; | ||
|
||
int avgCount = 100; | ||
}; | ||
|
||
} // namespace lime |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
#pragma once | ||
|
||
#include <string.h> | ||
#include <vector> | ||
#include <mutex> | ||
|
||
template<class T> class RingBuffer | ||
{ | ||
public: | ||
RingBuffer(int32_t count) | ||
: headIndex(0) | ||
, tailIndex(0) | ||
, size(0) | ||
, capacity(count) | ||
{ | ||
buffer.resize(count); | ||
} | ||
|
||
int Consume(T* dest, int32_t count) | ||
{ | ||
std::lock_guard<std::mutex> lck(mMutex); | ||
int consumed = 0; | ||
count = std::min(count, size); | ||
|
||
if (headIndex + count >= capacity) | ||
{ | ||
int toCopy = capacity - headIndex; | ||
memcpy(dest, &buffer[headIndex], toCopy * sizeof(T)); | ||
dest += toCopy; | ||
headIndex = 0; | ||
size -= toCopy; | ||
count -= toCopy; | ||
consumed += toCopy; | ||
} | ||
memcpy(dest, &buffer[headIndex], count * sizeof(T)); | ||
headIndex += count; | ||
size -= count; | ||
consumed += count; | ||
return consumed; | ||
} | ||
|
||
int Produce(const T* src, int32_t count) | ||
{ | ||
std::lock_guard<std::mutex> lck(mMutex); | ||
count = std::min(count, capacity - size); | ||
int produced = 0; | ||
|
||
if (tailIndex + count >= capacity) | ||
{ | ||
int toCopy = capacity - tailIndex; | ||
memcpy(&buffer[tailIndex], src, toCopy * sizeof(T)); | ||
src += toCopy; | ||
count -= toCopy; | ||
size += toCopy; | ||
produced += toCopy; | ||
tailIndex = 0; | ||
} | ||
memcpy(&buffer[tailIndex], src, count * sizeof(T)); | ||
tailIndex += count; | ||
size += count; | ||
produced += count; | ||
return produced; | ||
} | ||
|
||
private: | ||
std::mutex mMutex; | ||
std::vector<T> buffer; | ||
int headIndex; | ||
int tailIndex; | ||
int size; | ||
int capacity; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.