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:
Georgi Gerganov
2021-02-27 11:45:01 +02:00
committed by GitHub
parent 553b414929
commit 5b5293d764
6 changed files with 693 additions and 204 deletions

View File

@@ -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) {

View File

@@ -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

View File

@@ -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();

View File

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

View File

@@ -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) {