From ef081f4856f0b9b401f2e46429fd76ba622a7f83 Mon Sep 17 00:00:00 2001 From: Georgi Gerganov Date: Sun, 13 Dec 2020 16:24:44 +0200 Subject: [PATCH] ggwave-gui : adding tx waveform display --- examples/ggwave-common-sdl2.cpp | 2 +- examples/ggwave-gui/common.cpp | 86 ++++++++++++++++++++++++++++++++- include/ggwave/ggwave.h | 5 ++ src/ggwave.cpp | 45 +++++++++++------ 4 files changed, 120 insertions(+), 18 deletions(-) diff --git a/examples/ggwave-common-sdl2.cpp b/examples/ggwave-common-sdl2.cpp index 6089dc9..0aab83d 100644 --- a/examples/ggwave-common-sdl2.cpp +++ b/examples/ggwave-common-sdl2.cpp @@ -120,7 +120,7 @@ bool GGWave_init( playbackSpec.freq = ::kBaseSampleRate; playbackSpec.format = AUDIO_S16SYS; playbackSpec.channels = 1; - playbackSpec.samples = 16*1024; + playbackSpec.samples = 1024; playbackSpec.callback = NULL; SDL_zero(g_obtainedSpecOut); diff --git a/examples/ggwave-gui/common.cpp b/examples/ggwave-gui/common.cpp index 7b0305c..7a051ce 100644 --- a/examples/ggwave-gui/common.cpp +++ b/examples/ggwave-gui/common.cpp @@ -49,6 +49,7 @@ struct State { struct Flags { bool newMessage = false; bool newSpectrum = false; + bool newTxAmplitudeData = false; bool newStats = false; void clear() { memset(this, 0, sizeof(Flags)); } @@ -69,6 +70,12 @@ struct State { dst.spectrum = std::move(this->spectrum); } + if (this->flags.newTxAmplitudeData) { + dst.update = true; + dst.flags.newTxAmplitudeData = true; + dst.txAmplitudeData = std::move(this->txAmplitudeData); + } + if (this->flags.newStats) { dst.update = true; dst.flags.newStats = true; @@ -81,6 +88,7 @@ struct State { Message message; GGWave::SpectrumData spectrum; + GGWave::AmplitudeData16 txAmplitudeData; GGWaveStats stats; }; @@ -174,6 +182,11 @@ std::thread initMain() { g_buffer.stateCore.flags.newSpectrum = true; } + if (g_ggWave->takeTxAmplitudeData16(g_buffer.stateCore.txAmplitudeData)) { + g_buffer.stateCore.update = true; + g_buffer.stateCore.flags.newTxAmplitudeData = true; + } + if (true) { g_buffer.stateCore.update = true; g_buffer.stateCore.flags.newStats = true; @@ -227,9 +240,12 @@ void renderMain() { static double tStartInput = 0.0f; static double tEndInput = -100.0f; + static double tStartTx = 0.0f; + static double tLengthTx = 0.0f; static GGWaveStats statsCurrent; static GGWave::SpectrumData spectrumCurrent; + static GGWave::AmplitudeData16 txAmplitudeDataCurrent; static std::vector messageHistory; if (stateCurrent.update) { @@ -240,6 +256,25 @@ void renderMain() { if (stateCurrent.flags.newSpectrum) { spectrumCurrent = std::move(stateCurrent.spectrum); } + if (stateCurrent.flags.newTxAmplitudeData) { + txAmplitudeDataCurrent = std::move(stateCurrent.txAmplitudeData); + + tStartTx = ImGui::GetTime(); + tLengthTx = txAmplitudeDataCurrent.size()/g_ggWave->getSampleRateOut(); + { + auto & ampl = txAmplitudeDataCurrent; + int nBins = 512; + int nspb = ampl.size()/nBins; + for (int i = 0; i < nBins; ++i) { + double sum = 0.0; + for (int j = 0; j < nspb; ++j) { + sum += std::abs(ampl[i*nspb + j]); + } + ampl[i] = sum/nspb; + } + ampl.resize(nBins); + } + } if (stateCurrent.flags.newStats) { statsCurrent = std::move(stateCurrent.stats); } @@ -499,7 +534,56 @@ void renderMain() { { ImGui::GetContentRegionAvailWidth(), ImGui::GetTextLineHeight() }); } } else { - ImGui::TextDisabled("Listening for waves ...\n"); + static float amax = 0.0f; + static float frac = 0.0f; + + amax = 0.0f; + frac = (ImGui::GetTime() - tStartTx)/tLengthTx; + + if (txAmplitudeDataCurrent.size() && frac <= 1.0f) { + struct Funcs { + static float Sample(void * data, int i) { + auto res = std::fabs(((int16_t *)(data))[i]) ; + if (res > amax) amax = res; + return res; + } + + static float SampleFrac(void * data, int i) { + if (i > frac*txAmplitudeDataCurrent.size()) { + return 0.0f; + } + return std::fabs(((int16_t *)(data))[i]); + } + }; + + auto posSave = ImGui::GetCursorScreenPos(); + auto wSize = ImGui::GetContentRegionAvail(); + wSize.y = ImGui::GetTextLineHeight(); + + ImGui::PushStyleColor(ImGuiCol_FrameBg, { 0.3f, 0.3f, 0.3f, 0.3f }); + ImGui::PushStyleColor(ImGuiCol_PlotHistogram, ImGui::GetStyleColorVec4(ImGuiCol_TextDisabled)); + ImGui::PlotHistogram("##plotSpectrumCurrent", + Funcs::Sample, + txAmplitudeDataCurrent.data(), + txAmplitudeDataCurrent.size(), 0, + (std::string("")).c_str(), + 0.0f, FLT_MAX, wSize); + ImGui::PopStyleColor(2); + + ImGui::SetCursorScreenPos(posSave); + + ImGui::PushStyleColor(ImGuiCol_FrameBg, { 0.0f, 0.0f, 0.0f, 0.0f }); + ImGui::PushStyleColor(ImGuiCol_PlotHistogram, { 0.0f, 1.0f, 0.0f, 1.0f }); + ImGui::PlotHistogram("##plotSpectrumCurrent", + Funcs::SampleFrac, + txAmplitudeDataCurrent.data(), + txAmplitudeDataCurrent.size(), 0, + (std::string("")).c_str(), + 0.0f, amax, wSize); + ImGui::PopStyleColor(2); + } else { + ImGui::TextDisabled("Listening for waves ...\n"); + } } if (doInputFocus) { diff --git a/include/ggwave/ggwave.h b/include/ggwave/ggwave.h index f99694e..119af09 100644 --- a/include/ggwave/ggwave.h +++ b/include/ggwave/ggwave.h @@ -78,6 +78,7 @@ public: const int & getRxProtocolId() const { return m_rxProtocolId; } int takeRxData(TxRxData & dst); + int takeTxAmplitudeData16(AmplitudeData16 & dst); bool takeSpectrum(SpectrumData & dst); private: @@ -148,4 +149,8 @@ private: TxRxData m_txDataEncoded; TxProtocol m_txProtocol; + + AmplitudeData m_outputBlock; + AmplitudeData16 m_outputBlock16; + AmplitudeData16 m_txAmplitudeData16; }; diff --git a/src/ggwave.cpp b/src/ggwave.cpp index 00e29bc..2489eab 100644 --- a/src/ggwave.cpp +++ b/src/ggwave.cpp @@ -159,7 +159,9 @@ GGWave::GGWave( m_sampleAmplitudeHistory(kMaxSpectrumHistory), m_recordedAmplitude(kMaxRecordedFrames*kMaxSamplesPerFrame), m_txData(kMaxDataSize), - m_txDataEncoded(kMaxDataSize) + m_txDataEncoded(kMaxDataSize), + m_outputBlock(kMaxSamplesPerFrame), + m_outputBlock16(kMaxRecordedFrames*kMaxSamplesPerFrame) { init(0, "", getDefultTxProtocol(), 0); } @@ -224,9 +226,6 @@ void GGWave::send(const CBQueueAudio & cbQueueAudio) { int frameId = 0; - AmplitudeData outputBlock(kMaxSamplesPerFrame); - AmplitudeData16 outputBlock16(kMaxRecordedFrames*kMaxSamplesPerFrame); - std::vector phaseOffsets(kMaxDataBits); for (int k = 0; k < (int) phaseOffsets.size(); ++k) { @@ -273,7 +272,7 @@ void GGWave::send(const CBQueueAudio & cbQueueAudio) { rsData.Encode(m_txData.data() + 1, m_txDataEncoded.data() + m_encodedDataOffset); while (m_hasNewTxData) { - std::fill(outputBlock.begin(), outputBlock.end(), 0.0f); + std::fill(m_outputBlock.begin(), m_outputBlock.end(), 0.0f); if (m_sampleRateOut != m_sampleRateIn) { for (int k = 0; k < m_txProtocol.nDataBitsPerTx(); ++k) { @@ -299,9 +298,9 @@ void GGWave::send(const CBQueueAudio & cbQueueAudio) { for (int i = 0; i < m_nBitsInMarker; ++i) { if (i%2 == 0) { - ::addAmplitudeSmooth(bit1Amplitude[i], outputBlock, m_sendVolume, 0, samplesPerFrameOut, frameId, m_nMarkerFrames); + ::addAmplitudeSmooth(bit1Amplitude[i], m_outputBlock, m_sendVolume, 0, samplesPerFrameOut, frameId, m_nMarkerFrames); } else { - ::addAmplitudeSmooth(bit0Amplitude[i], outputBlock, m_sendVolume, 0, samplesPerFrameOut, frameId, m_nMarkerFrames); + ::addAmplitudeSmooth(bit0Amplitude[i], m_outputBlock, m_sendVolume, 0, samplesPerFrameOut, frameId, m_nMarkerFrames); } } } else if (frameId < m_nMarkerFrames + m_nPostMarkerFrames) { @@ -309,9 +308,9 @@ void GGWave::send(const CBQueueAudio & cbQueueAudio) { for (int i = 0; i < m_nBitsInMarker; ++i) { if (i%2 == 0) { - ::addAmplitudeSmooth(bit0Amplitude[i], outputBlock, m_sendVolume, 0, samplesPerFrameOut, frameId - m_nMarkerFrames, m_nPostMarkerFrames); + ::addAmplitudeSmooth(bit0Amplitude[i], m_outputBlock, m_sendVolume, 0, samplesPerFrameOut, frameId - m_nMarkerFrames, m_nPostMarkerFrames); } else { - ::addAmplitudeSmooth(bit1Amplitude[i], outputBlock, m_sendVolume, 0, samplesPerFrameOut, frameId - m_nMarkerFrames, m_nPostMarkerFrames); + ::addAmplitudeSmooth(bit1Amplitude[i], m_outputBlock, m_sendVolume, 0, samplesPerFrameOut, frameId - m_nMarkerFrames, m_nPostMarkerFrames); } } } else if (frameId < @@ -340,9 +339,9 @@ void GGWave::send(const CBQueueAudio & cbQueueAudio) { ++nFreq; if (k%2) { - ::addAmplitudeSmooth(bit0Amplitude[k/2], outputBlock, m_sendVolume, 0, samplesPerFrameOut, cycleModMain, m_txProtocol.framesPerTx); + ::addAmplitudeSmooth(bit0Amplitude[k/2], m_outputBlock, m_sendVolume, 0, samplesPerFrameOut, cycleModMain, m_txProtocol.framesPerTx); } else { - ::addAmplitudeSmooth(bit1Amplitude[k/2], outputBlock, m_sendVolume, 0, samplesPerFrameOut, cycleModMain, m_txProtocol.framesPerTx); + ::addAmplitudeSmooth(bit1Amplitude[k/2], m_outputBlock, m_sendVolume, 0, samplesPerFrameOut, cycleModMain, m_txProtocol.framesPerTx); } } } else if (frameId < @@ -354,9 +353,9 @@ void GGWave::send(const CBQueueAudio & cbQueueAudio) { int fId = frameId - ((m_nMarkerFrames + m_nPostMarkerFrames) + ((m_sendDataLength + m_nECCBytesPerTx)/m_txProtocol.bytesPerTx + 2)*m_txProtocol.framesPerTx); for (int i = 0; i < m_nBitsInMarker; ++i) { if (i%2 == 0) { - addAmplitudeSmooth(bit0Amplitude[i], outputBlock, m_sendVolume, 0, samplesPerFrameOut, fId, m_nMarkerFrames); + addAmplitudeSmooth(bit0Amplitude[i], m_outputBlock, m_sendVolume, 0, samplesPerFrameOut, fId, m_nMarkerFrames); } else { - addAmplitudeSmooth(bit1Amplitude[i], outputBlock, m_sendVolume, 0, samplesPerFrameOut, fId, m_nMarkerFrames); + addAmplitudeSmooth(bit1Amplitude[i], m_outputBlock, m_sendVolume, 0, samplesPerFrameOut, fId, m_nMarkerFrames); } } } else { @@ -366,18 +365,23 @@ void GGWave::send(const CBQueueAudio & cbQueueAudio) { if (nFreq == 0) nFreq = 1; float scale = 1.0f/nFreq; for (int i = 0; i < samplesPerFrameOut; ++i) { - outputBlock[i] *= scale; + m_outputBlock[i] *= scale; } // todo : support for non-int16 output for (int i = 0; i < samplesPerFrameOut; ++i) { - outputBlock16[frameId*samplesPerFrameOut + i] = std::round(32000.0*outputBlock[i]); + m_outputBlock16[frameId*samplesPerFrameOut + i] = std::round(32000.0*m_outputBlock[i]); } ++frameId; } - cbQueueAudio(outputBlock16.data(), frameId*samplesPerFrameOut*m_sampleSizeBytesOut); + cbQueueAudio(m_outputBlock16.data(), frameId*samplesPerFrameOut*m_sampleSizeBytesOut); + + m_txAmplitudeData16.resize(frameId*samplesPerFrameOut); + for (int i = 0; i < frameId*samplesPerFrameOut; ++i) { + m_txAmplitudeData16[i] = m_outputBlock16[i]; + } } void GGWave::receive(const CBDequeueAudio & CBDequeueAudio) { @@ -653,6 +657,15 @@ int GGWave::takeRxData(TxRxData & dst) { return res; } +int GGWave::takeTxAmplitudeData16(AmplitudeData16 & dst) { + if (m_txAmplitudeData16.size() == 0) return 0; + + auto res = m_txAmplitudeData16.size(); + dst = std::move(m_txAmplitudeData16); + + return res; +} + bool GGWave::takeSpectrum(SpectrumData & dst) { if (m_hasNewSpectrum == false) return false;