mirror of
https://github.com/ggerganov/ggwave.git
synced 2026-02-06 08:37:59 +08:00
Waver v1.4.0 (#23)
* waver : change default protocol * waver : global scale to 1.25 * waver : more colors * waver : resend last message * waver : bigger send button * waver : start with fixed-length enabled * waver : fix spectrum display for fixed-length mode * waver : add option to control input sample rate offset * ggwave : improve fixed-length decoding * examples-common : SDL now captures 1024 samples instead of 4096 * waver : spectrum improvements + spectrogram mode * minor changes * ggwave : add FFT static method * waver : realtime spectrum display * waver : fix dragging behavior * waver : show current tx protocol in "Messages" * waver : show frequency range and bandwidth of selected protocol * waver : change sample rate offset function output instead of input * waver : add info about local peers * waver : add option to pause spectrum by clicking it * waver : minor * waver : add option descriptions * waver : fix data races * waver : purple-ish background * waver : cian-ish background * waver : add option button to cancel receiving * waver : add option to select Rx Protocols * waver : change default protocol * waver : fix displayed protocol for each message
This commit is contained in:
@@ -164,7 +164,7 @@ bool GGWave_init(
|
||||
captureSpec = g_obtainedSpecOut;
|
||||
captureSpec.freq = GGWave::kBaseSampleRate + sampleRateOffset;
|
||||
captureSpec.format = AUDIO_F32SYS;
|
||||
captureSpec.samples = 4096;
|
||||
captureSpec.samples = 1024;
|
||||
|
||||
SDL_zero(g_obtainedSpecInp);
|
||||
|
||||
@@ -228,7 +228,7 @@ bool GGWave_init(
|
||||
return true;
|
||||
}
|
||||
|
||||
GGWave * GGWave_instance() { return g_ggWave; }
|
||||
GGWave *& GGWave_instance() { return g_ggWave; }
|
||||
|
||||
bool GGWave_mainLoop() {
|
||||
if (g_devIdInp == 0 && g_devIdOut == 0) {
|
||||
|
||||
@@ -8,6 +8,6 @@ class GGWave;
|
||||
|
||||
void GGWave_setDefaultCaptureDeviceName(std::string name);
|
||||
bool GGWave_init(const int playbackId, const int captureId, const int payloadLength = -1, const int sampleRateOffset = 0);
|
||||
GGWave * GGWave_instance();
|
||||
GGWave *& GGWave_instance();
|
||||
bool GGWave_mainLoop();
|
||||
bool GGWave_deinit();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -18,6 +18,8 @@
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
const float kGlobalImGuiScale = 1.25f;
|
||||
|
||||
// ImGui helpers
|
||||
|
||||
bool ImGui_tryLoadFont(const std::string & filename, float size = 14.0f, bool merge = false) {
|
||||
@@ -106,7 +108,7 @@ bool ImGui_SetStyle() {
|
||||
|
||||
style.Colors[ImGuiCol_Text] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
|
||||
style.Colors[ImGuiCol_TextDisabled] = ImVec4(0.24f, 0.41f, 0.41f, 1.00f);
|
||||
style.Colors[ImGuiCol_WindowBg] = ImVec4(0.08f, 0.08f, 0.08f, 0.94f);
|
||||
style.Colors[ImGuiCol_WindowBg] = ImVec4(0.11f, 0.15f, 0.20f, 0.60f);
|
||||
//style.Colors[ImGuiCol_ChildWindowBg] = ImVec4(0.07f, 0.07f, 0.09f, 1.00f);
|
||||
style.Colors[ImGuiCol_PopupBg] = ImVec4(0.07f, 0.07f, 0.09f, 1.00f);
|
||||
style.Colors[ImGuiCol_Border] = ImVec4(0.31f, 0.31f, 0.31f, 0.71f);
|
||||
@@ -148,6 +150,8 @@ bool ImGui_SetStyle() {
|
||||
style.Colors[ImGuiCol_TextSelectedBg] = ImVec4(0.25f, 1.00f, 0.00f, 0.43f);
|
||||
style.Colors[ImGuiCol_ModalWindowDarkening] = ImVec4(1.00f, 0.98f, 0.95f, 0.78f);
|
||||
|
||||
style.ScaleAllSizes(kGlobalImGuiScale);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -222,13 +226,13 @@ int main(int argc, char** argv) {
|
||||
ImGui_Init(window, gl_context);
|
||||
ImGui::GetIO().IniFilename = nullptr;
|
||||
|
||||
ImGui_tryLoadFont(getBinaryPath() + "DroidSans.ttf", 14.0f, false);
|
||||
ImGui_tryLoadFont(getBinaryPath() + "../examples/assets/fonts/DroidSans.ttf", 14.0f, false);
|
||||
ImGui_tryLoadFont(getBinaryPath() + "../../examples/assets/fonts/DroidSans.ttf", 14.0f, false);
|
||||
ImGui_tryLoadFont(getBinaryPath() + "DroidSans.ttf", kGlobalImGuiScale*14.0f, false);
|
||||
ImGui_tryLoadFont(getBinaryPath() + "../examples/assets/fonts/DroidSans.ttf", kGlobalImGuiScale*14.0f, false);
|
||||
ImGui_tryLoadFont(getBinaryPath() + "../../examples/assets/fonts/DroidSans.ttf", kGlobalImGuiScale*14.0f, false);
|
||||
|
||||
ImGui_tryLoadFont(getBinaryPath() + "fontawesome-webfont.ttf", 14.0f, true);
|
||||
ImGui_tryLoadFont(getBinaryPath() + "../examples/assets/fonts/fontawesome-webfont.ttf", 14.0f, true);
|
||||
ImGui_tryLoadFont(getBinaryPath() + "../../examples/assets/fonts/fontawesome-webfont.ttf", 14.0f, true);
|
||||
ImGui_tryLoadFont(getBinaryPath() + "fontawesome-webfont.ttf", kGlobalImGuiScale*14.0f, true);
|
||||
ImGui_tryLoadFont(getBinaryPath() + "../examples/assets/fonts/fontawesome-webfont.ttf", kGlobalImGuiScale*14.0f, true);
|
||||
ImGui_tryLoadFont(getBinaryPath() + "../../examples/assets/fonts/fontawesome-webfont.ttf", kGlobalImGuiScale*14.0f, true);
|
||||
|
||||
ImGui_SetStyle();
|
||||
|
||||
@@ -300,10 +304,10 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
|
||||
deinitMain();
|
||||
GGWave_deinit();
|
||||
|
||||
worker.join();
|
||||
|
||||
GGWave_deinit();
|
||||
|
||||
// Cleanup
|
||||
ImGui_Shutdown();
|
||||
ImGui::DestroyContext();
|
||||
|
||||
@@ -238,6 +238,7 @@ public:
|
||||
using Parameters = ggwave_Parameters;
|
||||
using SampleFormat = ggwave_SampleFormat;
|
||||
using TxProtocolId = ggwave_TxProtocolId;
|
||||
using RxProtocolId = ggwave_TxProtocolId;
|
||||
|
||||
struct TxProtocol {
|
||||
const char * name; // string identifier of the protocol
|
||||
@@ -249,7 +250,10 @@ public:
|
||||
int nDataBitsPerTx() const { return 8*bytesPerTx; }
|
||||
};
|
||||
|
||||
using RxProtocol = TxProtocol;
|
||||
|
||||
using TxProtocols = std::map<TxProtocolId, TxProtocol>;
|
||||
using RxProtocols = std::map<RxProtocolId, RxProtocol>;
|
||||
|
||||
static const TxProtocols & getTxProtocols() {
|
||||
static const TxProtocols kTxProtocols {
|
||||
@@ -337,6 +341,8 @@ public:
|
||||
|
||||
const float & getSampleRateInp() const { return m_sampleRateInp; }
|
||||
const float & getSampleRateOut() const { return m_sampleRateOut; }
|
||||
const SampleFormat & getSampleFormatInp() const { return m_sampleFormatInp; }
|
||||
const SampleFormat & getSampleFormatOut() const { return m_sampleFormatOut; }
|
||||
|
||||
// Tx
|
||||
|
||||
@@ -345,18 +351,31 @@ public:
|
||||
static const TxProtocol & getTxProtocol(int id) { return getTxProtocols().at(TxProtocolId(id)); }
|
||||
static const TxProtocol & getTxProtocol(TxProtocolId id) { return getTxProtocols().at(id); }
|
||||
|
||||
int takeTxAmplitudeDataI16(AmplitudeDataI16 & dst);
|
||||
bool takeTxAmplitudeI16(AmplitudeDataI16 & dst);
|
||||
|
||||
// Rx
|
||||
|
||||
void setRxProtocols(const TxProtocols & rxProtocols) { m_rxProtocols = rxProtocols; }
|
||||
bool stopReceiving();
|
||||
void setRxProtocols(const RxProtocols & rxProtocols) { m_rxProtocols = rxProtocols; }
|
||||
const RxProtocols & getRxProtocols() const { return m_rxProtocols; }
|
||||
|
||||
const TxRxData & getRxData() const { return m_rxData; }
|
||||
const TxProtocol & getRxProtocol() const { return m_rxProtocol; }
|
||||
const TxProtocolId & getRxProtocolId() const { return m_rxProtocolId; }
|
||||
const RxProtocol & getRxProtocol() const { return m_rxProtocol; }
|
||||
const RxProtocolId & getRxProtocolId() const { return m_rxProtocolId; }
|
||||
|
||||
int takeRxData(TxRxData & dst);
|
||||
bool takeSpectrum(SpectrumData & dst);
|
||||
bool takeRxSpectrum(SpectrumData & dst);
|
||||
bool takeRxAmplitude(AmplitudeData & dst);
|
||||
|
||||
// compute FFT of real values
|
||||
//
|
||||
// src - input real-valued data, size is N
|
||||
// dst - output complex-valued data, size is 2*N
|
||||
//
|
||||
// d is scaling factor
|
||||
// N must be <= kMaxSamplesPerFrame
|
||||
//
|
||||
static bool computeFFTR(const float * src, float * dst, int N, float d);
|
||||
|
||||
private:
|
||||
void decode_fixed();
|
||||
@@ -413,6 +432,7 @@ private:
|
||||
std::vector<float> m_fftOut; // complex
|
||||
|
||||
bool m_hasNewSpectrum;
|
||||
bool m_hasNewAmplitude;
|
||||
SpectrumData m_sampleSpectrum;
|
||||
AmplitudeData m_sampleAmplitude;
|
||||
AmplitudeData m_sampleAmplitudeResampled;
|
||||
|
||||
@@ -153,7 +153,7 @@ int reverse(int N, int n) {
|
||||
}
|
||||
|
||||
void ordina(float * f1, int N) {
|
||||
float f2[2*GGWave::kMaxSamplesPerFrame];
|
||||
static thread_local float f2[2*GGWave::kMaxSamplesPerFrame];
|
||||
for (int i = 0; i < N; i++) {
|
||||
int ir = reverse(N, i);
|
||||
f2[2*i + 0] = f1[2*ir + 0];
|
||||
@@ -210,7 +210,7 @@ void FFT(float * f, int N, float d) {
|
||||
}
|
||||
}
|
||||
|
||||
void FFT(float * src, float * dst, int N, float d) {
|
||||
void FFT(const float * src, float * dst, int N, float d) {
|
||||
for (int i = 0; i < N; ++i) {
|
||||
dst[2*i + 0] = src[i];
|
||||
dst[2*i + 1] = 0.0f;
|
||||
@@ -309,6 +309,7 @@ GGWave::GGWave(const Parameters & parameters) :
|
||||
m_fftInp(kMaxSamplesPerFrame),
|
||||
m_fftOut(2*kMaxSamplesPerFrame),
|
||||
m_hasNewSpectrum(false),
|
||||
m_hasNewAmplitude(false),
|
||||
m_sampleSpectrum(kMaxSamplesPerFrame),
|
||||
m_sampleAmplitude(kMaxSamplesPerFrame + 128), // small extra space because sometimes resampling needs a few more samples
|
||||
m_sampleAmplitudeResampled(8*kMaxSamplesPerFrame), // min input sampling rate is 0.125*kBaseSampleRate
|
||||
@@ -798,6 +799,8 @@ void GGWave::decode(const CBWaveformInp & cbWaveformInp) {
|
||||
|
||||
// we have enough bytes to do analysis
|
||||
if (nSamplesRecorded >= m_samplesPerFrame) {
|
||||
m_hasNewAmplitude = true;
|
||||
|
||||
if (m_isFixedPayloadLength) {
|
||||
decode_fixed();
|
||||
} else {
|
||||
@@ -817,6 +820,24 @@ void GGWave::decode(const CBWaveformInp & cbWaveformInp) {
|
||||
}
|
||||
}
|
||||
|
||||
bool GGWave::takeTxAmplitudeI16(AmplitudeDataI16 & dst) {
|
||||
if (m_txAmplitudeDataI16.size() == 0) return false;
|
||||
|
||||
dst = std::move(m_txAmplitudeDataI16);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GGWave::stopReceiving() {
|
||||
if (m_receivingData == false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_receivingData = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int GGWave::takeRxData(TxRxData & dst) {
|
||||
if (m_lastRxDataLength == 0) return 0;
|
||||
|
||||
@@ -830,16 +851,7 @@ int GGWave::takeRxData(TxRxData & dst) {
|
||||
return res;
|
||||
}
|
||||
|
||||
int GGWave::takeTxAmplitudeDataI16(AmplitudeDataI16 & dst) {
|
||||
if (m_txAmplitudeDataI16.size() == 0) return 0;
|
||||
|
||||
int res = (int) m_txAmplitudeDataI16.size();
|
||||
dst = std::move(m_txAmplitudeDataI16);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool GGWave::takeSpectrum(SpectrumData & dst) {
|
||||
bool GGWave::takeRxSpectrum(SpectrumData & dst) {
|
||||
if (m_hasNewSpectrum == false) return false;
|
||||
|
||||
m_hasNewSpectrum = false;
|
||||
@@ -848,6 +860,26 @@ bool GGWave::takeSpectrum(SpectrumData & dst) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GGWave::takeRxAmplitude(AmplitudeData & dst) {
|
||||
if (m_hasNewAmplitude == false) return false;
|
||||
|
||||
m_hasNewAmplitude = false;
|
||||
dst = m_sampleAmplitude;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GGWave::computeFFTR(const float * src, float * dst, int N, float d) {
|
||||
if (N > kMaxSamplesPerFrame) {
|
||||
fprintf(stderr, "computeFFTR: N (%d) must be <= %d\n", N, GGWave::kMaxSamplesPerFrame);
|
||||
return false;
|
||||
}
|
||||
|
||||
FFT(src, dst, N, d);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// Variable payload length
|
||||
//
|
||||
@@ -875,9 +907,7 @@ void GGWave::decode_variable() {
|
||||
}
|
||||
|
||||
// calculate spectrum
|
||||
std::copy(m_sampleAmplitudeAverage.begin(), m_sampleAmplitudeAverage.begin() + m_samplesPerFrame, m_fftInp.data());
|
||||
|
||||
FFT(m_fftInp.data(), m_fftOut.data(), m_samplesPerFrame, 1.0);
|
||||
FFT(m_sampleAmplitudeAverage.data(), m_fftOut.data(), m_samplesPerFrame, 1.0);
|
||||
|
||||
for (int i = 0; i < m_samplesPerFrame; ++i) {
|
||||
m_sampleSpectrum[i] = (m_fftOut[2*i + 0]*m_fftOut[2*i + 0] + m_fftOut[2*i + 1]*m_fftOut[2*i + 1]);
|
||||
@@ -1144,10 +1174,10 @@ void GGWave::decode_variable() {
|
||||
// Fixed payload length
|
||||
|
||||
void GGWave::decode_fixed() {
|
||||
// calculate spectrum
|
||||
std::copy(m_sampleAmplitude.begin(), m_sampleAmplitude.begin() + m_samplesPerFrame, m_fftInp.data());
|
||||
m_hasNewSpectrum = true;
|
||||
|
||||
FFT(m_fftInp.data(), m_fftOut.data(), m_samplesPerFrame, 1.0);
|
||||
// calculate spectrum
|
||||
FFT(m_sampleAmplitude.data(), m_fftOut.data(), m_samplesPerFrame, 1.0);
|
||||
|
||||
for (int i = 0; i < m_samplesPerFrame; ++i) {
|
||||
m_sampleSpectrum[i] = (m_fftOut[2*i + 0]*m_fftOut[2*i + 0] + m_fftOut[2*i + 1]*m_fftOut[2*i + 1]);
|
||||
@@ -1188,6 +1218,8 @@ void GGWave::decode_fixed() {
|
||||
std::vector<ToneData> tones(nTones);
|
||||
|
||||
bool detectedSignal = true;
|
||||
int txDetectedTotal = 0;
|
||||
int txNeededTotal = 0;
|
||||
for (int k = 0; k < totalTxs; ++k) {
|
||||
for (auto & tone : tones) {
|
||||
std::fill(tone.nMax, tone.nMax + 16, 0);
|
||||
@@ -1231,7 +1263,7 @@ void GGWave::decode_fixed() {
|
||||
}
|
||||
}
|
||||
|
||||
int detectedTx = 0;
|
||||
int txDetected = 0;
|
||||
int txNeeded = 0;
|
||||
for (int j = 0; j < rxProtocol.bytesPerTx; ++j) {
|
||||
if (k*rxProtocol.bytesPerTx + j >= totalLength) break;
|
||||
@@ -1239,18 +1271,25 @@ void GGWave::decode_fixed() {
|
||||
for (int b = 0; b < 16; ++b) {
|
||||
if (tones[2*j + 0].nMax[b] > rxProtocol.framesPerTx/2) {
|
||||
detectedBins[2*(k*rxProtocol.bytesPerTx + j) + 0] = b;
|
||||
detectedTx++;
|
||||
txDetected++;
|
||||
}
|
||||
if (tones[2*j + 1].nMax[b] > rxProtocol.framesPerTx/2) {
|
||||
detectedBins[2*(k*rxProtocol.bytesPerTx + j) + 1] = b;
|
||||
detectedTx++;
|
||||
txDetected++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (detectedTx < txNeeded) {
|
||||
detectedSignal = false;
|
||||
}
|
||||
txDetectedTotal += txDetected;
|
||||
txNeededTotal += txNeeded;
|
||||
}
|
||||
|
||||
//if (rxProtocolId == GGWAVE_TX_PROTOCOL_DT_FAST) {
|
||||
// printf("detected = %d, needed = %d\n", txDetectedTotal, txNeededTotal);
|
||||
//}
|
||||
|
||||
if (txDetectedTotal < 0.75*txNeededTotal) {
|
||||
detectedSignal = false;
|
||||
}
|
||||
|
||||
if (detectedSignal) {
|
||||
|
||||
Reference in New Issue
Block a user