From 429ff0f1b11ba7e3eab42040ead7ac6bb9f79479 Mon Sep 17 00:00:00 2001 From: Georgi Gerganov Date: Fri, 4 Dec 2020 22:27:04 +0200 Subject: [PATCH 01/12] wip : receive all types of protocols --- CMakeLists.txt | 4 +- examples/ggwave-cli/main.cpp | 36 +---- examples/ggwave-common-sdl2.cpp | 25 +-- examples/ggwave-gui/main.cpp | 38 +---- include/ggwave/ggwave.h | 70 ++++++--- src/ggwave.cpp | 271 +++++++++++++++----------------- 6 files changed, 188 insertions(+), 256 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fdccbda..1473703 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,8 +47,8 @@ if (GGWAVE_SANITIZE_THREAD) endif() if (GGWAVE_SANITIZE_ADDRESS) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer -D_GLIBCXX_DEBUG") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer -D_GLIBCXX_DEBUG") endif() if (GGWAVE_SANITIZE_UNDEFINED) diff --git a/examples/ggwave-cli/main.cpp b/examples/ggwave-cli/main.cpp index 09c605a..2bd2cb9 100644 --- a/examples/ggwave-cli/main.cpp +++ b/examples/ggwave-cli/main.cpp @@ -36,40 +36,6 @@ int main(int argc, char** argv) { ggWave->setTxMode(GGWave::TxMode::VariableLength); printf("Selecting Tx protocol %d\n", txProtocol); - switch (txProtocol) { - case 0: - { - printf("Using 'Normal' Tx Protocol\n"); - ggWave->setParameters(1, 40, 9, 3, 50); - } - break; - case 1: - { - printf("Using 'Fast' Tx Protocol\n"); - ggWave->setParameters(1, 40, 6, 3, 50); - } - break; - case 2: - { - printf("Using 'Fastest' Tx Protocol\n"); - ggWave->setParameters(1, 40, 3, 3, 50); - } - break; - case 3: - { - printf("Using 'Ultrasonic' Tx Protocol\n"); - ggWave->setParameters(1, 320, 9, 3, 50); - } - break; - default: - { - printf("Using 'Fast' Tx Protocol\n"); - ggWave->setParameters(1, 40, 6, 3, 50); - } - }; - printf("\n"); - - ggWave->init(0, ""); std::mutex mutex; std::thread inputThread([&]() { @@ -86,7 +52,7 @@ int main(int argc, char** argv) { } { std::lock_guard lock(mutex); - ggWave->init(input.size(), input.data()); + ggWave->init(input.size(), input.data(), ggWave->getTxProtocols()[txProtocol]); } inputOld = input; } diff --git a/examples/ggwave-common-sdl2.cpp b/examples/ggwave-common-sdl2.cpp index 271015d..2cdc39a 100644 --- a/examples/ggwave-common-sdl2.cpp +++ b/examples/ggwave-common-sdl2.cpp @@ -34,8 +34,8 @@ GGWave *g_ggWave = nullptr; // JS interface extern "C" { EMSCRIPTEN_KEEPALIVE - int setText(int textLength, const char * text) { - g_ggWave->init(textLength, text); + int setText(int textLength, const char * text, int protocolId) { + g_ggWave->init(textLength, text, g_ggWave->getTxProtocols()[protocolId]); return 0; } @@ -77,29 +77,8 @@ extern "C" { EMSCRIPTEN_KEEPALIVE int setTxMode(int txMode) { g_ggWave->setTxMode((GGWave::TxMode)(txMode)); - g_ggWave->init(0, ""); return 0; } - - EMSCRIPTEN_KEEPALIVE - void setParameters( - int paramFreqDelta, - int paramFreqStart, - int paramFramesPerTx, - int paramBytesPerTx, - int /*paramECCBytesPerTx*/, - int paramVolume) { - if (g_ggWave == nullptr) return; - - g_ggWave->setParameters( - paramFreqDelta, - paramFreqStart, - paramFramesPerTx, - paramBytesPerTx, - paramVolume); - - g_ggWave->init(0, ""); - } } void GGWave_setDefaultCaptureDeviceName(std::string name) { diff --git a/examples/ggwave-gui/main.cpp b/examples/ggwave-gui/main.cpp index 130087c..18cdbef 100644 --- a/examples/ggwave-gui/main.cpp +++ b/examples/ggwave-gui/main.cpp @@ -71,42 +71,6 @@ int main(int argc, char** argv) { ggWave->setTxMode(GGWave::TxMode::VariableLength); - printf("Selecting Tx protocol %d\n", txProtocol); - switch (txProtocol) { - case 0: - { - printf("Using 'Normal' Tx Protocol\n"); - ggWave->setParameters(1, 40, 9, 3, 50); - } - break; - case 1: - { - printf("Using 'Fast' Tx Protocol\n"); - ggWave->setParameters(1, 40, 6, 3, 50); - } - break; - case 2: - { - printf("Using 'Fastest' Tx Protocol\n"); - ggWave->setParameters(1, 40, 3, 3, 50); - } - break; - case 3: - { - printf("Using 'Ultrasonic' Tx Protocol\n"); - ggWave->setParameters(1, 320, 9, 3, 50); - } - break; - default: - { - printf("Using 'Fast' Tx Protocol\n"); - ggWave->setParameters(1, 40, 6, 3, 50); - } - }; - printf("\n"); - - ggWave->init(0, ""); - if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_GAMECONTROLLER) != 0) { fprintf(stderr, "Error: %s\n", SDL_GetError()); return -1; @@ -175,7 +139,7 @@ int main(int argc, char** argv) { } if (inputCurrent.update) { - ggWave->init(inputCurrent.message.data.size(), inputCurrent.message.data.data()); + ggWave->init(inputCurrent.message.data.size(), inputCurrent.message.data.data(), ggWave->getTxProtocols()[2]); inputCurrent.update = false; } diff --git a/include/ggwave/ggwave.h b/include/ggwave/ggwave.h index 4a1e27c..24eff1a 100644 --- a/include/ggwave/ggwave.h +++ b/include/ggwave/ggwave.h @@ -4,6 +4,7 @@ #include #include #include +#include namespace RS { class ReedSolomon; @@ -24,11 +25,23 @@ public: static constexpr auto kMaxRecordedFrames = 64*10; static constexpr auto kDefaultFixedLength = 82; + struct TxProtocol { + const char * name; + + int paramFreqDelta; + int paramFreqStart; + int paramFramesPerTx; + int paramBytesPerTx; + int paramECCBytesPerTx; + int paramVolume; + }; + using AmplitudeData = std::array; using AmplitudeData16 = std::array; using SpectrumData = std::array; using RecordedData = std::array; using TxRxData = std::array; + using TxProtocols = std::vector; using CBQueueAudio = std::function; using CBDequeueAudio = std::function; @@ -41,17 +54,12 @@ public: int aSampleSizeBytesOut); ~GGWave(); - void setTxMode(TxMode aTxMode) { txMode = aTxMode; } - - bool setParameters( - int aParamFreqDelta, - int aParamFreqStart, - int aParamFramesPerTx, - int aParamBytesPerTx, - int aParamVolume); - - bool init(int textLength, const char * stext); + void setTxMode(TxMode aTxMode) { + txMode = aTxMode; + init(0, "", getDefultTxProtocol()); + } + bool init(int textLength, const char * stext, const TxProtocol & aProtocol); void send(const CBQueueAudio & cbQueueAudio); void receive(const CBDequeueAudio & CBDequeueAudio); @@ -70,6 +78,8 @@ public: const float & getAverageRxTime_ms() const { return averageRxTime_ms; } const TxRxData & getRxData() const { return rxData; } + const TxProtocol & getDefultTxProtocol() const { return txProtocols[1]; } + const TxProtocols & getTxProtocols() const { return txProtocols; } int takeRxData(TxRxData & dst) { if (lastRxDataLength == 0) return 0; @@ -82,14 +92,38 @@ public: } private: - int nIterations; + const TxProtocols txProtocols { + { "Normal", 1, 40, 9, 3, 32, 50 }, + { "Fast", 1, 40, 6, 3, 32, 50 }, + { "Fastest", 1, 40, 3, 3, 32, 50 }, + { "Ultrasonic", 1, 320, 9, 3, 32, 50 }, + }; - int paramFreqDelta = 6; - int paramFreqStart = 40; - int paramFramesPerTx = 6; - int paramBytesPerTx = 2; - int paramECCBytesPerTx = 32; // used for fixed-length Tx - int paramVolume = 10; + int maxFramesPerTx() const { + int res = 0; + for (const auto & protocol : txProtocols) { + res = std::max(res, protocol.paramFramesPerTx); + } + return res; + } + + int minBytesPerTx() const { + int res = txProtocols.front().paramFramesPerTx; + for (const auto & protocol : txProtocols) { + res = std::min(res, protocol.paramBytesPerTx); + } + return res; + } + + int maxECCBytesPerTx() const { + int res = 0; + for (const auto & protocol : txProtocols) { + res = std::max(res, protocol.paramECCBytesPerTx); + } + return res; + } + + int nIterations; // Rx bool receivingData; @@ -135,7 +169,6 @@ private: int frameId; int framesLeftToAnalyze; int framesLeftToRecord; - int framesPerTx; int framesToAnalyze; int framesToRecord; int freqDelta_bin = 1; @@ -152,6 +185,7 @@ private: std::string textToSend; TxMode txMode = TxMode::FixedLength; + TxProtocol txProtocol; AmplitudeData outputBlock; AmplitudeData16 outputBlock16; diff --git a/src/ggwave.cpp b/src/ggwave.cpp index ce0c086..b15721b 100644 --- a/src/ggwave.cpp +++ b/src/ggwave.cpp @@ -121,7 +121,7 @@ GGWave::GGWave( sampleSizeBytesIn = aSampleSizeBytesIn; sampleSizeBytesOut = aSampleSizeBytesOut; - init(0, ""); + setTxMode(TxMode::VariableLength); } GGWave::~GGWave() { @@ -129,41 +129,26 @@ GGWave::~GGWave() { if (rsLength) delete rsLength; } -bool GGWave::setParameters( - int aParamFreqDelta, - int aParamFreqStart, - int aParamFramesPerTx, - int aParamBytesPerTx, - int aParamVolume) { - - paramFreqDelta = aParamFreqDelta; - paramFreqStart = aParamFreqStart; - paramFramesPerTx = aParamFramesPerTx; - paramBytesPerTx = aParamBytesPerTx; - paramVolume = aParamVolume; - - return true; -} - -bool GGWave::init(int textLength, const char * stext) { +bool GGWave::init(int textLength, const char * stext, const TxProtocol & aProtocol) { if (textLength > kMaxLength) { printf("Truncating data from %d to 140 bytes\n", textLength); textLength = kMaxLength; } + txProtocol = aProtocol; + const uint8_t * text = reinterpret_cast(stext); frameId = 0; nIterations = 0; hasData = false; isamplesPerFrame = 1.0f/samplesPerFrame; - sendVolume = ((double)(paramVolume))/100.0f; + sendVolume = ((double)(txProtocol.paramVolume))/100.0f; hzPerFrame = sampleRateIn/samplesPerFrame; ihzPerFrame = 1.0/hzPerFrame; - framesPerTx = paramFramesPerTx; - nDataBitsPerTx = paramBytesPerTx*8; - nECCBytesPerTx = (txMode == TxMode::FixedLength) ? paramECCBytesPerTx : getECCBytesForLength(textLength); + nDataBitsPerTx = txProtocol.paramBytesPerTx*8; + nECCBytesPerTx = (txMode == TxMode::FixedLength) ? txProtocol.paramECCBytesPerTx : getECCBytesForLength(textLength); framesToAnalyze = 0; framesLeftToAnalyze = 0; @@ -174,10 +159,10 @@ bool GGWave::init(int textLength, const char * stext) { nPostMarkerFrames = 0; sendDataLength = (txMode == TxMode::FixedLength) ? kDefaultFixedLength : textLength + 3; - freqDelta_bin = paramFreqDelta/2; - freqDelta_hz = hzPerFrame*paramFreqDelta; - freqStart_hz = hzPerFrame*paramFreqStart; - if (paramFreqDelta == 1) { + freqDelta_bin = txProtocol.paramFreqDelta/2; + freqDelta_hz = hzPerFrame*txProtocol.paramFreqDelta; + freqStart_hz = hzPerFrame*txProtocol.paramFreqStart; + if (txProtocol.paramFreqDelta == 1) { freqDelta_bin = 1; freqDelta_hz *= 2; } @@ -244,7 +229,6 @@ bool GGWave::init(int textLength, const char * stext) { analyzingData = false; sampleAmplitude.fill(0); - sampleSpectrum.fill(0); for (auto & s : sampleAmplitudeHistory) { s.fill(0); @@ -311,15 +295,15 @@ void GGWave::send(const CBQueueAudio & cbQueueAudio) { } } else if (frameId < (nMarkerFrames + nPostMarkerFrames) + - ((sendDataLength + nECCBytesPerTx)/nBytesPerTx + 2)*framesPerTx) { + ((sendDataLength + nECCBytesPerTx)/nBytesPerTx + 2)*txProtocol.paramFramesPerTx) { int dataOffset = frameId - nMarkerFrames - nPostMarkerFrames; - int cycleModMain = dataOffset%framesPerTx; - dataOffset /= framesPerTx; + int cycleModMain = dataOffset%txProtocol.paramFramesPerTx; + dataOffset /= txProtocol.paramFramesPerTx; dataOffset *= nBytesPerTx; dataBits.fill(0); - if (paramFreqDelta > 1) { + if (txProtocol.paramFreqDelta > 1) { for (int j = 0; j < nBytesPerTx; ++j) { for (int i = 0; i < 8; ++i) { dataBits[j*8 + i] = txDataEncoded[dataOffset + j] & (1 << i); @@ -329,10 +313,10 @@ void GGWave::send(const CBQueueAudio & cbQueueAudio) { for (int k = 0; k < nDataBitsPerTx; ++k) { ++nFreq; if (dataBits[k] == false) { - ::addAmplitudeSmooth(bit0Amplitude[k], outputBlock, sendVolume, 0, samplesPerFrameOut, cycleModMain, framesPerTx); + ::addAmplitudeSmooth(bit0Amplitude[k], outputBlock, sendVolume, 0, samplesPerFrameOut, cycleModMain, txProtocol.paramFramesPerTx); continue; } - ::addAmplitudeSmooth(bit1Amplitude[k], outputBlock, sendVolume, 0, samplesPerFrameOut, cycleModMain, framesPerTx); + ::addAmplitudeSmooth(bit1Amplitude[k], outputBlock, sendVolume, 0, samplesPerFrameOut, cycleModMain, txProtocol.paramFramesPerTx); } } else { for (int j = 0; j < nBytesPerTx; ++j) { @@ -351,19 +335,19 @@ void GGWave::send(const CBQueueAudio & cbQueueAudio) { ++nFreq; if (k%2) { - ::addAmplitudeSmooth(bit0Amplitude[k/2], outputBlock, sendVolume, 0, samplesPerFrameOut, cycleModMain, framesPerTx); + ::addAmplitudeSmooth(bit0Amplitude[k/2], outputBlock, sendVolume, 0, samplesPerFrameOut, cycleModMain, txProtocol.paramFramesPerTx); } else { - ::addAmplitudeSmooth(bit1Amplitude[k/2], outputBlock, sendVolume, 0, samplesPerFrameOut, cycleModMain, framesPerTx); + ::addAmplitudeSmooth(bit1Amplitude[k/2], outputBlock, sendVolume, 0, samplesPerFrameOut, cycleModMain, txProtocol.paramFramesPerTx); } } } } else if (txMode == TxMode::VariableLength && frameId < (nMarkerFrames + nPostMarkerFrames) + - ((sendDataLength + nECCBytesPerTx)/nBytesPerTx + 2)*framesPerTx + + ((sendDataLength + nECCBytesPerTx)/nBytesPerTx + 2)*txProtocol.paramFramesPerTx + (nMarkerFrames)) { nFreq = nBitsInMarker; - int fId = frameId - ((nMarkerFrames + nPostMarkerFrames) + ((sendDataLength + nECCBytesPerTx)/nBytesPerTx + 2)*framesPerTx); + int fId = frameId - ((nMarkerFrames + nPostMarkerFrames) + ((sendDataLength + nECCBytesPerTx)/nBytesPerTx + 2)*txProtocol.paramFramesPerTx); for (int i = 0; i < nBitsInMarker; ++i) { if (i%2 == 0) { addAmplitudeSmooth(bit0Amplitude[i], outputBlock, sendVolume, 0, samplesPerFrameOut, fId, nMarkerFrames); @@ -401,6 +385,7 @@ void GGWave::receive(const CBDequeueAudio & CBDequeueAudio) { // todo : support for non-float input auto nBytesRecorded = CBDequeueAudio(sampleAmplitude.data(), samplesPerFrame*sampleSizeBytesIn); + if (nBytesRecorded != 0) { { sampleAmplitudeHistory[historyId] = sampleAmplitude; @@ -448,132 +433,136 @@ void GGWave::receive(const CBDequeueAudio & CBDequeueAudio) { recordedAmplitude.data() + (framesToRecord - framesLeftToRecord)*samplesPerFrame); if (--framesLeftToRecord <= 0) { - std::fill(sampleSpectrum.begin(), sampleSpectrum.end(), 0.0f); analyzingData = true; } } } if (analyzingData) { - int nBytesPerTx = nDataBitsPerTx/8; - int stepsPerFrame = 16; - int step = samplesPerFrame/stepsPerFrame; - - int offsetStart = 0; - - framesToAnalyze = nMarkerFrames*stepsPerFrame; - framesLeftToAnalyze = framesToAnalyze; + const int stepsPerFrame = 16; + const int step = samplesPerFrame/stepsPerFrame; bool isValid = false; - for (int ii = nMarkerFrames*stepsPerFrame - 1; ii >= nMarkerFrames*stepsPerFrame/2; --ii) { - offsetStart = ii; - bool knownLength = txMode == TxMode::FixedLength; - int encodedOffset = (txMode == TxMode::FixedLength) ? 0 : 3; + for (const auto & rxProtocol : txProtocols) { + std::fill(sampleSpectrum.begin(), sampleSpectrum.end(), 0.0f); - for (int itx = 0; itx < 1024; ++itx) { - int offsetTx = offsetStart + itx*framesPerTx*stepsPerFrame; - if (offsetTx >= recvDuration_frames*stepsPerFrame) { - break; - } + framesToAnalyze = nMarkerFrames*stepsPerFrame; + framesLeftToAnalyze = framesToAnalyze; + for (int ii = nMarkerFrames*stepsPerFrame - 1; ii >= nMarkerFrames*stepsPerFrame/2; --ii) { + bool knownLength = txMode == TxMode::FixedLength; - std::copy( - recordedAmplitude.begin() + offsetTx*step, - recordedAmplitude.begin() + offsetTx*step + samplesPerFrame, fftIn.data()); + const int offsetStart = ii; + const int encodedOffset = (knownLength) ? 0 : 3; + + for (int itx = 0; itx < 1024; ++itx) { + int offsetTx = offsetStart + itx*rxProtocol.paramFramesPerTx*stepsPerFrame; + if (offsetTx >= recvDuration_frames*stepsPerFrame || (itx + 1)*rxProtocol.paramBytesPerTx >= (int) txDataEncoded.size()) { + break; + } + + std::copy( + recordedAmplitude.begin() + offsetTx*step, + recordedAmplitude.begin() + offsetTx*step + samplesPerFrame, fftIn.data()); + + for (int k = 1; k < rxProtocol.paramFramesPerTx - 1; ++k) { + for (int i = 0; i < samplesPerFrame; ++i) { + fftIn[i] += recordedAmplitude[(offsetTx + k*stepsPerFrame)*step + i]; + } + } + + FFT(fftIn.data(), fftOut.data(), samplesPerFrame, 1.0); - for (int k = 1; k < framesPerTx-1; ++k) { for (int i = 0; i < samplesPerFrame; ++i) { - fftIn[i] += recordedAmplitude[(offsetTx + k*stepsPerFrame)*step + i]; + sampleSpectrum[i] = (fftOut[i].real()*fftOut[i].real() + fftOut[i].imag()*fftOut[i].imag()); } - } - - FFT(fftIn.data(), fftOut.data(), samplesPerFrame, 1.0); - - for (int i = 0; i < samplesPerFrame; ++i) { - sampleSpectrum[i] = (fftOut[i].real()*fftOut[i].real() + fftOut[i].imag()*fftOut[i].imag()); - } - for (int i = 1; i < samplesPerFrame/2; ++i) { - sampleSpectrum[i] += sampleSpectrum[samplesPerFrame - i]; - } - - uint8_t curByte = 0; - if (paramFreqDelta > 1) { - for (int i = 0; i < nDataBitsPerTx; ++i) { - int k = i%8; - int bin = std::round(dataFreqs_hz[i]*ihzPerFrame); - if (sampleSpectrum[bin] > 1*sampleSpectrum[bin + freqDelta_bin]) { - curByte += 1 << k; - } else if (sampleSpectrum[bin + freqDelta_bin] > 1*sampleSpectrum[bin]) { - } else { - } - if (k == 7) { - txDataEncoded[itx*nBytesPerTx + i/8] = curByte; - curByte = 0; - } + for (int i = 1; i < samplesPerFrame/2; ++i) { + sampleSpectrum[i] += sampleSpectrum[samplesPerFrame - i]; } - } else { - for (int i = 0; i < 2*nBytesPerTx; ++i) { - int bin = std::round(dataFreqs_hz[0]*ihzPerFrame) + i*16; - int kmax = 0; - double amax = 0.0; - for (int k = 0; k < 16; ++k) { - if (sampleSpectrum[bin + k] > amax) { - kmax = k; - amax = sampleSpectrum[bin + k]; + uint8_t curByte = 0; + if (rxProtocol.paramFreqDelta > 1) { + for (int i = 0; i < nDataBitsPerTx; ++i) { + int k = i%8; + int bin = std::round(dataFreqs_hz[i]*ihzPerFrame); + if (sampleSpectrum[bin] > 1*sampleSpectrum[bin + freqDelta_bin]) { + curByte += 1 << k; + } else if (sampleSpectrum[bin + freqDelta_bin] > 1*sampleSpectrum[bin]) { + } else { + } + if (k == 7) { + txDataEncoded[itx*rxProtocol.paramBytesPerTx + i/8] = curByte; + curByte = 0; } } - - if (i%2) { - curByte += (kmax << 4); - txDataEncoded[itx*nBytesPerTx + i/2] = curByte; - curByte = 0; - } else { - curByte = kmax; - } - } - } - - if (txMode == TxMode::VariableLength) { - if (itx*nBytesPerTx > 3 && knownLength == false) { - if ((rsLength->Decode(txDataEncoded.data(), rxData.data()) == 0) && (rxData[0] <= 140)) { - knownLength = true; - } else { - break; - } - } - } - } - - if (txMode == TxMode::VariableLength && knownLength) { - if (rsData) delete rsData; - rsData = new RS::ReedSolomon(rxData[0], ::getECCBytesForLength(rxData[0])); - } - - if (knownLength) { - int decodedLength = rxData[0]; - if (rsData->Decode(txDataEncoded.data() + encodedOffset, rxData.data()) == 0) { - printf("Decoded length = %d\n", decodedLength); - if (txMode == TxMode::FixedLength && rxData[0] == 'A') { - printf("[ANSWER] Received sound data successfully!\n"); - } else if (txMode == TxMode::FixedLength && rxData[0] == 'O') { - printf("[OFFER] Received sound data successfully!\n"); } else { - std::string s((char *) rxData.data(), decodedLength); - printf("Received sound data successfully: '%s'\n", s.c_str()); + for (int i = 0; i < 2*rxProtocol.paramBytesPerTx; ++i) { + int bin = std::round(dataFreqs_hz[0]*ihzPerFrame) + i*16; + + int kmax = 0; + double amax = 0.0; + for (int k = 0; k < 16; ++k) { + if (sampleSpectrum[bin + k] > amax) { + kmax = k; + amax = sampleSpectrum[bin + k]; + } + } + + if (i%2) { + curByte += (kmax << 4); + txDataEncoded[itx*rxProtocol.paramBytesPerTx + i/2] = curByte; + curByte = 0; + } else { + curByte = kmax; + } + } + } + + if (txMode == TxMode::VariableLength) { + if (itx*rxProtocol.paramBytesPerTx > 3 && knownLength == false) { + if ((rsLength->Decode(txDataEncoded.data(), rxData.data()) == 0) && (rxData[0] > 0 && rxData[0] <= 140)) { + knownLength = true; + } else { + break; + } + } } - hasNewRxData = true; - lastRxDataLength = decodedLength; - framesToRecord = 0; - isValid = true; } + + if (txMode == TxMode::VariableLength && knownLength) { + if (rsData) delete rsData; + rsData = new RS::ReedSolomon(rxData[0], ::getECCBytesForLength(rxData[0])); + } + + if (knownLength) { + int decodedLength = rxData[0]; + if (rsData->Decode(txDataEncoded.data() + encodedOffset, rxData.data()) == 0) { + if (txMode == TxMode::FixedLength && rxData[0] == 'A') { + printf("[ANSWER] Received sound data successfully!\n"); + } else if (txMode == TxMode::FixedLength && rxData[0] == 'O') { + printf("[OFFER] Received sound data successfully!\n"); + } else if (rxData[0] != 0) { + printf("Decoded length = %d\n", decodedLength); + std::string s((char *) rxData.data(), decodedLength); + printf("Received sound data successfully: '%s'\n", s.c_str()); + + isValid = true; + hasNewRxData = true; + lastRxDataLength = decodedLength; + } + } + } + + if (isValid) { + break; + } + --framesLeftToAnalyze; } - if (isValid) { - break; - } - --framesLeftToAnalyze; + if (isValid) break; } + framesToRecord = 0; + if (isValid == false) { printf("Failed to capture sound data. Please try again\n"); framesToRecord = -1; @@ -608,9 +597,9 @@ void GGWave::receive(const CBDequeueAudio & CBDequeueAudio) { rxData.fill(0); receivingData = true; if (txMode == TxMode::FixedLength) { - recvDuration_frames = nMarkerFrames + nPostMarkerFrames + framesPerTx*((kDefaultFixedLength + paramECCBytesPerTx)/paramBytesPerTx + 1); + recvDuration_frames = nMarkerFrames + nPostMarkerFrames + maxFramesPerTx()*((kDefaultFixedLength + maxECCBytesPerTx())/minBytesPerTx() + 1); } else { - recvDuration_frames = nMarkerFrames + nPostMarkerFrames + framesPerTx*((kMaxLength + ::getECCBytesForLength(kMaxLength))/paramBytesPerTx + 1); + recvDuration_frames = nMarkerFrames + nPostMarkerFrames + maxFramesPerTx()*((kMaxLength + ::getECCBytesForLength(kMaxLength))/minBytesPerTx() + 1); } framesToRecord = recvDuration_frames; framesLeftToRecord = recvDuration_frames; From 1fcbdfbe999eb3d1ccdc3bb39cb80e9462ef6509 Mon Sep 17 00:00:00 2001 From: Georgi Gerganov Date: Sat, 5 Dec 2020 09:45:31 +0200 Subject: [PATCH 02/12] wip : remove paramFreqDelta --- include/ggwave/ggwave.h | 13 ++--- src/ggwave.cpp | 122 +++++++++++++++------------------------- 2 files changed, 51 insertions(+), 84 deletions(-) diff --git a/include/ggwave/ggwave.h b/include/ggwave/ggwave.h index 24eff1a..49a85e9 100644 --- a/include/ggwave/ggwave.h +++ b/include/ggwave/ggwave.h @@ -28,7 +28,6 @@ public: struct TxProtocol { const char * name; - int paramFreqDelta; int paramFreqStart; int paramFramesPerTx; int paramBytesPerTx; @@ -93,10 +92,10 @@ public: private: const TxProtocols txProtocols { - { "Normal", 1, 40, 9, 3, 32, 50 }, - { "Fast", 1, 40, 6, 3, 32, 50 }, - { "Fastest", 1, 40, 3, 3, 32, 50 }, - { "Ultrasonic", 1, 320, 9, 3, 32, 50 }, + { "Normal", 40, 9, 3, 32, 50 }, + { "Fast", 40, 6, 3, 32, 50 }, + { "Fastest", 40, 3, 3, 32, 50 }, + { "Ultrasonic", 320, 9, 3, 32, 50 }, }; int maxFramesPerTx() const { @@ -159,8 +158,8 @@ private: float freqDelta_hz; float freqStart_hz; - float hzPerFrame; - float ihzPerFrame; + float hzPerSample; + float ihzPerSample; float isamplesPerFrame; float sampleRateIn; float sampleRateOut; diff --git a/src/ggwave.cpp b/src/ggwave.cpp index b15721b..d2449b4 100644 --- a/src/ggwave.cpp +++ b/src/ggwave.cpp @@ -144,10 +144,10 @@ bool GGWave::init(int textLength, const char * stext, const TxProtocol & aProtoc isamplesPerFrame = 1.0f/samplesPerFrame; sendVolume = ((double)(txProtocol.paramVolume))/100.0f; - hzPerFrame = sampleRateIn/samplesPerFrame; - ihzPerFrame = 1.0/hzPerFrame; + hzPerSample = sampleRateIn/samplesPerFrame; + ihzPerSample = 1.0/hzPerSample; - nDataBitsPerTx = txProtocol.paramBytesPerTx*8; + nDataBitsPerTx = 8*txProtocol.paramBytesPerTx; nECCBytesPerTx = (txMode == TxMode::FixedLength) ? txProtocol.paramECCBytesPerTx : getECCBytesForLength(textLength); framesToAnalyze = 0; @@ -159,13 +159,9 @@ bool GGWave::init(int textLength, const char * stext, const TxProtocol & aProtoc nPostMarkerFrames = 0; sendDataLength = (txMode == TxMode::FixedLength) ? kDefaultFixedLength : textLength + 3; - freqDelta_bin = txProtocol.paramFreqDelta/2; - freqDelta_hz = hzPerFrame*txProtocol.paramFreqDelta; - freqStart_hz = hzPerFrame*txProtocol.paramFreqStart; - if (txProtocol.paramFreqDelta == 1) { - freqDelta_bin = 1; - freqDelta_hz *= 2; - } + freqDelta_bin = 1; + freqDelta_hz = 2*hzPerSample; + freqStart_hz = hzPerSample*txProtocol.paramFreqStart; outputBlock.fill(0); @@ -195,7 +191,7 @@ bool GGWave::init(int textLength, const char * stext, const TxProtocol & aProtoc } for (int i = 0; i < samplesPerFrame; i++) { double curi = i; - bit0Amplitude[k][i] = std::sin((2.0*M_PI)*(curi*isamplesPerFrame)*((freq + hzPerFrame*freqDelta_bin)*curIHzPerFrame) + phaseOffset); + bit0Amplitude[k][i] = std::sin((2.0*M_PI)*(curi*isamplesPerFrame)*((freq + hzPerSample*freqDelta_bin)*curIHzPerFrame) + phaseOffset); } } @@ -268,7 +264,7 @@ void GGWave::send(const CBQueueAudio & cbQueueAudio) { } for (int i = 0; i < samplesPerFrameOut; i++) { double curi = (i + frameId*samplesPerFrameOut); - bit0Amplitude[k][i] = std::sin((2.0*M_PI)*(curi*isamplesPerFrame)*((freq + hzPerFrame*freqDelta_bin)*curIHzPerFrame) + phaseOffset); + bit0Amplitude[k][i] = std::sin((2.0*M_PI)*(curi*isamplesPerFrame)*((freq + hzPerSample*freqDelta_bin)*curIHzPerFrame) + phaseOffset); } } } @@ -303,42 +299,25 @@ void GGWave::send(const CBQueueAudio & cbQueueAudio) { dataBits.fill(0); - if (txProtocol.paramFreqDelta > 1) { - for (int j = 0; j < nBytesPerTx; ++j) { - for (int i = 0; i < 8; ++i) { - dataBits[j*8 + i] = txDataEncoded[dataOffset + j] & (1 << i); - } + for (int j = 0; j < nBytesPerTx; ++j) { + { + uint8_t d = txDataEncoded[dataOffset + j] & 15; + dataBits[(2*j + 0)*16 + d] = 1; } - - for (int k = 0; k < nDataBitsPerTx; ++k) { - ++nFreq; - if (dataBits[k] == false) { - ::addAmplitudeSmooth(bit0Amplitude[k], outputBlock, sendVolume, 0, samplesPerFrameOut, cycleModMain, txProtocol.paramFramesPerTx); - continue; - } - ::addAmplitudeSmooth(bit1Amplitude[k], outputBlock, sendVolume, 0, samplesPerFrameOut, cycleModMain, txProtocol.paramFramesPerTx); - } - } else { - for (int j = 0; j < nBytesPerTx; ++j) { - { - uint8_t d = txDataEncoded[dataOffset + j] & 15; - dataBits[(2*j + 0)*16 + d] = 1; - } - { - uint8_t d = txDataEncoded[dataOffset + j] & 240; - dataBits[(2*j + 1)*16 + (d >> 4)] = 1; - } + { + uint8_t d = txDataEncoded[dataOffset + j] & 240; + dataBits[(2*j + 1)*16 + (d >> 4)] = 1; } + } - for (int k = 0; k < 2*nBytesPerTx*16; ++k) { - if (dataBits[k] == 0) continue; + for (int k = 0; k < 2*nBytesPerTx*16; ++k) { + if (dataBits[k] == 0) continue; - ++nFreq; - if (k%2) { - ::addAmplitudeSmooth(bit0Amplitude[k/2], outputBlock, sendVolume, 0, samplesPerFrameOut, cycleModMain, txProtocol.paramFramesPerTx); - } else { - ::addAmplitudeSmooth(bit1Amplitude[k/2], outputBlock, sendVolume, 0, samplesPerFrameOut, cycleModMain, txProtocol.paramFramesPerTx); - } + ++nFreq; + if (k%2) { + ::addAmplitudeSmooth(bit0Amplitude[k/2], outputBlock, sendVolume, 0, samplesPerFrameOut, cycleModMain, txProtocol.paramFramesPerTx); + } else { + ::addAmplitudeSmooth(bit1Amplitude[k/2], outputBlock, sendVolume, 0, samplesPerFrameOut, cycleModMain, txProtocol.paramFramesPerTx); } } } else if (txMode == TxMode::VariableLength && frameId < @@ -439,6 +418,8 @@ void GGWave::receive(const CBDequeueAudio & CBDequeueAudio) { } if (analyzingData) { + auto tStart = std::chrono::high_resolution_clock::now(); + const int stepsPerFrame = 16; const int step = samplesPerFrame/stepsPerFrame; @@ -480,40 +461,24 @@ void GGWave::receive(const CBDequeueAudio & CBDequeueAudio) { } uint8_t curByte = 0; - if (rxProtocol.paramFreqDelta > 1) { - for (int i = 0; i < nDataBitsPerTx; ++i) { - int k = i%8; - int bin = std::round(dataFreqs_hz[i]*ihzPerFrame); - if (sampleSpectrum[bin] > 1*sampleSpectrum[bin + freqDelta_bin]) { - curByte += 1 << k; - } else if (sampleSpectrum[bin + freqDelta_bin] > 1*sampleSpectrum[bin]) { - } else { - } - if (k == 7) { - txDataEncoded[itx*rxProtocol.paramBytesPerTx + i/8] = curByte; - curByte = 0; + for (int i = 0; i < 2*rxProtocol.paramBytesPerTx; ++i) { + int bin = std::round(dataFreqs_hz[0]*ihzPerSample) + i*16; + + int kmax = 0; + double amax = 0.0; + for (int k = 0; k < 16; ++k) { + if (sampleSpectrum[bin + k] > amax) { + kmax = k; + amax = sampleSpectrum[bin + k]; } } - } else { - for (int i = 0; i < 2*rxProtocol.paramBytesPerTx; ++i) { - int bin = std::round(dataFreqs_hz[0]*ihzPerFrame) + i*16; - int kmax = 0; - double amax = 0.0; - for (int k = 0; k < 16; ++k) { - if (sampleSpectrum[bin + k] > amax) { - kmax = k; - amax = sampleSpectrum[bin + k]; - } - } - - if (i%2) { - curByte += (kmax << 4); - txDataEncoded[itx*rxProtocol.paramBytesPerTx + i/2] = curByte; - curByte = 0; - } else { - curByte = kmax; - } + if (i%2) { + curByte += (kmax << 4); + txDataEncoded[itx*rxProtocol.paramBytesPerTx + i/2] = curByte; + curByte = 0; + } else { + curByte = kmax; } } @@ -575,6 +540,9 @@ void GGWave::receive(const CBDequeueAudio & CBDequeueAudio) { framesToAnalyze = 0; framesLeftToAnalyze = 0; + + auto tEnd = std::chrono::high_resolution_clock::now(); + printf("Time to analyze: %d ms\n", (int) std::chrono::duration_cast(tEnd - tStart).count()); } // check if receiving data @@ -582,7 +550,7 @@ void GGWave::receive(const CBDequeueAudio & CBDequeueAudio) { bool isReceiving = true; for (int i = 0; i < nBitsInMarker; ++i) { - int bin = std::round(dataFreqs_hz[i]*ihzPerFrame); + int bin = std::round(dataFreqs_hz[i]*ihzPerSample); if (i%2 == 0) { if (sampleSpectrum[bin] <= 3.0f*sampleSpectrum[bin + freqDelta_bin]) isReceiving = false; @@ -608,7 +576,7 @@ void GGWave::receive(const CBDequeueAudio & CBDequeueAudio) { bool isEnded = true; for (int i = 0; i < nBitsInMarker; ++i) { - int bin = std::round(dataFreqs_hz[i]*ihzPerFrame); + int bin = std::round(dataFreqs_hz[i]*ihzPerSample); if (i%2 == 0) { if (sampleSpectrum[bin] >= 3.0f*sampleSpectrum[bin + freqDelta_bin]) isEnded = false; From 09d0e41df45b20c81a71ef5dfba88221e733b4c7 Mon Sep 17 00:00:00 2001 From: Georgi Gerganov Date: Sat, 5 Dec 2020 10:03:48 +0200 Subject: [PATCH 03/12] wip : proper recording length --- include/ggwave/ggwave.h | 2 +- src/ggwave.cpp | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/include/ggwave/ggwave.h b/include/ggwave/ggwave.h index 49a85e9..9aa078a 100644 --- a/include/ggwave/ggwave.h +++ b/include/ggwave/ggwave.h @@ -22,7 +22,7 @@ public: static constexpr auto kMaxDataSize = 256; static constexpr auto kMaxLength = 140; static constexpr auto kMaxSpectrumHistory = 4; - static constexpr auto kMaxRecordedFrames = 64*10; + static constexpr auto kMaxRecordedFrames = 1024; static constexpr auto kDefaultFixedLength = 82; struct TxProtocol { diff --git a/src/ggwave.cpp b/src/ggwave.cpp index d2449b4..89567b6 100644 --- a/src/ggwave.cpp +++ b/src/ggwave.cpp @@ -418,6 +418,7 @@ void GGWave::receive(const CBDequeueAudio & CBDequeueAudio) { } if (analyzingData) { + printf("Analyzing captured data ..\n"); auto tStart = std::chrono::high_resolution_clock::now(); const int stepsPerFrame = 16; @@ -565,9 +566,9 @@ void GGWave::receive(const CBDequeueAudio & CBDequeueAudio) { rxData.fill(0); receivingData = true; if (txMode == TxMode::FixedLength) { - recvDuration_frames = nMarkerFrames + nPostMarkerFrames + maxFramesPerTx()*((kDefaultFixedLength + maxECCBytesPerTx())/minBytesPerTx() + 1); + recvDuration_frames = 2*nMarkerFrames + nPostMarkerFrames + maxFramesPerTx()*((kDefaultFixedLength + maxECCBytesPerTx())/minBytesPerTx() + 1); } else { - recvDuration_frames = nMarkerFrames + nPostMarkerFrames + maxFramesPerTx()*((kMaxLength + ::getECCBytesForLength(kMaxLength))/minBytesPerTx() + 1); + recvDuration_frames = 2*nMarkerFrames + nPostMarkerFrames + maxFramesPerTx()*((kMaxLength + ::getECCBytesForLength(kMaxLength))/minBytesPerTx() + 1); } framesToRecord = recvDuration_frames; framesLeftToRecord = recvDuration_frames; @@ -587,7 +588,7 @@ void GGWave::receive(const CBDequeueAudio & CBDequeueAudio) { if (isEnded && framesToRecord > 1) { std::time_t timestamp = std::time(nullptr); - printf("%sReceived end marker\n", std::asctime(std::localtime(×tamp))); + printf("%sReceived end marker. Frames left = %d\n", std::asctime(std::localtime(×tamp)), framesLeftToRecord); recvDuration_frames -= framesLeftToRecord - 1; framesLeftToRecord = 1; } From 9de063bf56af89ee1df6748775c7fee73222a966 Mon Sep 17 00:00:00 2001 From: Georgi Gerganov Date: Sat, 5 Dec 2020 10:17:19 +0200 Subject: [PATCH 04/12] wip : fix receive protocols with different start freq --- src/ggwave.cpp | 51 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 36 insertions(+), 15 deletions(-) diff --git a/src/ggwave.cpp b/src/ggwave.cpp index 89567b6..8661321 100644 --- a/src/ggwave.cpp +++ b/src/ggwave.cpp @@ -463,7 +463,8 @@ void GGWave::receive(const CBDequeueAudio & CBDequeueAudio) { uint8_t curByte = 0; for (int i = 0; i < 2*rxProtocol.paramBytesPerTx; ++i) { - int bin = std::round(dataFreqs_hz[0]*ihzPerSample) + i*16; + double freq = hzPerSample*rxProtocol.paramFreqStart; + int bin = std::round(freq*ihzPerSample) + i*16; int kmax = 0; double amax = 0.0; @@ -548,15 +549,25 @@ void GGWave::receive(const CBDequeueAudio & CBDequeueAudio) { // check if receiving data if (receivingData == false) { - bool isReceiving = true; + bool isReceiving = false; - for (int i = 0; i < nBitsInMarker; ++i) { - int bin = std::round(dataFreqs_hz[i]*ihzPerSample); + for (const auto & rxProtocol : txProtocols) { + bool isReceivingCur = true; - if (i%2 == 0) { - if (sampleSpectrum[bin] <= 3.0f*sampleSpectrum[bin + freqDelta_bin]) isReceiving = false; - } else { - if (sampleSpectrum[bin] >= 3.0f*sampleSpectrum[bin + freqDelta_bin]) isReceiving = false; + for (int i = 0; i < nBitsInMarker; ++i) { + double freq = hzPerSample*rxProtocol.paramFreqStart + freqDelta_hz*i; + int bin = std::round(freq*ihzPerSample); + + if (i%2 == 0) { + if (sampleSpectrum[bin] <= 3.0f*sampleSpectrum[bin + freqDelta_bin]) isReceivingCur = false; + } else { + if (sampleSpectrum[bin] >= 3.0f*sampleSpectrum[bin + freqDelta_bin]) isReceivingCur = false; + } + } + + if (isReceivingCur) { + isReceiving = true; + break; } } @@ -574,15 +585,25 @@ void GGWave::receive(const CBDequeueAudio & CBDequeueAudio) { framesLeftToRecord = recvDuration_frames; } } else if (txMode == TxMode::VariableLength) { - bool isEnded = true; + bool isEnded = false; - for (int i = 0; i < nBitsInMarker; ++i) { - int bin = std::round(dataFreqs_hz[i]*ihzPerSample); + for (const auto & rxProtocol : txProtocols) { + bool isEndedCur = true; - if (i%2 == 0) { - if (sampleSpectrum[bin] >= 3.0f*sampleSpectrum[bin + freqDelta_bin]) isEnded = false; - } else { - if (sampleSpectrum[bin] <= 3.0f*sampleSpectrum[bin + freqDelta_bin]) isEnded = false; + for (int i = 0; i < nBitsInMarker; ++i) { + double freq = hzPerSample*rxProtocol.paramFreqStart + freqDelta_hz*i; + int bin = std::round(freq*ihzPerSample); + + if (i%2 == 0) { + if (sampleSpectrum[bin] >= 3.0f*sampleSpectrum[bin + freqDelta_bin]) isEndedCur = false; + } else { + if (sampleSpectrum[bin] <= 3.0f*sampleSpectrum[bin + freqDelta_bin]) isEndedCur = false; + } + } + + if (isEndedCur) { + isEnded = true; + break; } } From d95e09faf4087660533c6a29419a616d409866cd Mon Sep 17 00:00:00 2001 From: Georgi Gerganov Date: Sat, 5 Dec 2020 10:54:02 +0200 Subject: [PATCH 05/12] wip : remove param ecc bytes --- include/ggwave/ggwave.h | 18 +++++------------- src/ggwave.cpp | 39 +++++++++++++++++++++++++-------------- 2 files changed, 30 insertions(+), 27 deletions(-) diff --git a/include/ggwave/ggwave.h b/include/ggwave/ggwave.h index 9aa078a..3496c90 100644 --- a/include/ggwave/ggwave.h +++ b/include/ggwave/ggwave.h @@ -24,6 +24,7 @@ public: static constexpr auto kMaxSpectrumHistory = 4; static constexpr auto kMaxRecordedFrames = 1024; static constexpr auto kDefaultFixedLength = 82; + static constexpr auto kDefaultFixedECCBytes = 32; struct TxProtocol { const char * name; @@ -31,7 +32,6 @@ public: int paramFreqStart; int paramFramesPerTx; int paramBytesPerTx; - int paramECCBytesPerTx; int paramVolume; }; @@ -92,10 +92,10 @@ public: private: const TxProtocols txProtocols { - { "Normal", 40, 9, 3, 32, 50 }, - { "Fast", 40, 6, 3, 32, 50 }, - { "Fastest", 40, 3, 3, 32, 50 }, - { "Ultrasonic", 320, 9, 3, 32, 50 }, + { "Normal", 40, 9, 3, 50 }, + { "Fast", 40, 6, 3, 50 }, + { "Fastest", 40, 3, 3, 50 }, + { "Ultrasonic", 320, 9, 3, 50 }, }; int maxFramesPerTx() const { @@ -114,14 +114,6 @@ private: return res; } - int maxECCBytesPerTx() const { - int res = 0; - for (const auto & protocol : txProtocols) { - res = std::max(res, protocol.paramECCBytesPerTx); - } - return res; - } - int nIterations; // Rx diff --git a/src/ggwave.cpp b/src/ggwave.cpp index 8661321..4ba6d65 100644 --- a/src/ggwave.cpp +++ b/src/ggwave.cpp @@ -148,7 +148,7 @@ bool GGWave::init(int textLength, const char * stext, const TxProtocol & aProtoc ihzPerSample = 1.0/hzPerSample; nDataBitsPerTx = 8*txProtocol.paramBytesPerTx; - nECCBytesPerTx = (txMode == TxMode::FixedLength) ? txProtocol.paramECCBytesPerTx : getECCBytesForLength(textLength); + nECCBytesPerTx = (txMode == TxMode::FixedLength) ? kDefaultFixedECCBytes : getECCBytesForLength(textLength); framesToAnalyze = 0; framesLeftToAnalyze = 0; @@ -199,7 +199,7 @@ bool GGWave::init(int textLength, const char * stext, const TxProtocol & aProtoc if (rsLength) delete rsLength; if (txMode == TxMode::FixedLength) { - rsData = new RS::ReedSolomon(kDefaultFixedLength, nECCBytesPerTx); + rsData = new RS::ReedSolomon(kDefaultFixedLength, kDefaultFixedLength); rsLength = nullptr; } else { rsData = new RS::ReedSolomon(textLength, nECCBytesPerTx); @@ -501,20 +501,31 @@ void GGWave::receive(const CBDequeueAudio & CBDequeueAudio) { } if (knownLength) { - int decodedLength = rxData[0]; - if (rsData->Decode(txDataEncoded.data() + encodedOffset, rxData.data()) == 0) { - if (txMode == TxMode::FixedLength && rxData[0] == 'A') { - printf("[ANSWER] Received sound data successfully!\n"); - } else if (txMode == TxMode::FixedLength && rxData[0] == 'O') { - printf("[OFFER] Received sound data successfully!\n"); - } else if (rxData[0] != 0) { - printf("Decoded length = %d\n", decodedLength); - std::string s((char *) rxData.data(), decodedLength); - printf("Received sound data successfully: '%s'\n", s.c_str()); + if (txMode == FixedLength) { + if (rsData->Decode(txDataEncoded.data() + encodedOffset, rxData.data()) == 0) { + if (rxData[0] == 'A') { + printf("[ANSWER] Received sound data successfully!\n"); + } else if (rxData[0] == 'O') { + printf("[OFFER] Received sound data successfully!\n"); + } isValid = true; hasNewRxData = true; - lastRxDataLength = decodedLength; + lastRxDataLength = kDefaultFixedLength; + } + } else { + int decodedLength = rxData[0]; + if (rsData->Decode(txDataEncoded.data() + encodedOffset, rxData.data()) == 0) { + if (rxData[0] != 0) { + std::string s((char *) rxData.data(), decodedLength); + + printf("Decoded length = %d\n", decodedLength); + printf("Received sound data successfully: '%s'\n", s.c_str()); + + isValid = true; + hasNewRxData = true; + lastRxDataLength = decodedLength; + } } } } @@ -577,7 +588,7 @@ void GGWave::receive(const CBDequeueAudio & CBDequeueAudio) { rxData.fill(0); receivingData = true; if (txMode == TxMode::FixedLength) { - recvDuration_frames = 2*nMarkerFrames + nPostMarkerFrames + maxFramesPerTx()*((kDefaultFixedLength + maxECCBytesPerTx())/minBytesPerTx() + 1); + recvDuration_frames = 2*nMarkerFrames + nPostMarkerFrames + maxFramesPerTx()*((kDefaultFixedLength + kDefaultFixedECCBytes)/minBytesPerTx() + 1); } else { recvDuration_frames = 2*nMarkerFrames + nPostMarkerFrames + maxFramesPerTx()*((kMaxLength + ::getECCBytesForLength(kMaxLength))/minBytesPerTx() + 1); } From a02ead3b906748b15bb2fbe16de7d56bf6c610c5 Mon Sep 17 00:00:00 2001 From: Georgi Gerganov Date: Sat, 5 Dec 2020 11:25:29 +0200 Subject: [PATCH 06/12] wip : remove TxMode always use variable length --- examples/ggwave-cli/main.cpp | 2 - examples/ggwave-common-sdl2.cpp | 6 - examples/ggwave-gui/main.cpp | 2 - include/ggwave/ggwave.h | 13 --- src/ggwave.cpp | 198 ++++++++++++++------------------ 5 files changed, 85 insertions(+), 136 deletions(-) diff --git a/examples/ggwave-cli/main.cpp b/examples/ggwave-cli/main.cpp index 2bd2cb9..bd53cc5 100644 --- a/examples/ggwave-cli/main.cpp +++ b/examples/ggwave-cli/main.cpp @@ -33,8 +33,6 @@ int main(int argc, char** argv) { auto ggWave = GGWave_instance(); - ggWave->setTxMode(GGWave::TxMode::VariableLength); - printf("Selecting Tx protocol %d\n", txProtocol); std::mutex mutex; diff --git a/examples/ggwave-common-sdl2.cpp b/examples/ggwave-common-sdl2.cpp index 2cdc39a..ba4242e 100644 --- a/examples/ggwave-common-sdl2.cpp +++ b/examples/ggwave-common-sdl2.cpp @@ -73,12 +73,6 @@ extern "C" { int doInit() { return GGWave_init(-1, -1); } - - EMSCRIPTEN_KEEPALIVE - int setTxMode(int txMode) { - g_ggWave->setTxMode((GGWave::TxMode)(txMode)); - return 0; - } } void GGWave_setDefaultCaptureDeviceName(std::string name) { diff --git a/examples/ggwave-gui/main.cpp b/examples/ggwave-gui/main.cpp index 18cdbef..fff1df8 100644 --- a/examples/ggwave-gui/main.cpp +++ b/examples/ggwave-gui/main.cpp @@ -69,8 +69,6 @@ int main(int argc, char** argv) { auto ggWave = GGWave_instance(); - ggWave->setTxMode(GGWave::TxMode::VariableLength); - if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_GAMECONTROLLER) != 0) { fprintf(stderr, "Error: %s\n", SDL_GetError()); return -1; diff --git a/include/ggwave/ggwave.h b/include/ggwave/ggwave.h index 3496c90..e1eba88 100644 --- a/include/ggwave/ggwave.h +++ b/include/ggwave/ggwave.h @@ -12,19 +12,12 @@ class ReedSolomon; class GGWave { public: - enum TxMode { - FixedLength = 0, - VariableLength, - }; - static constexpr auto kMaxSamplesPerFrame = 1024; static constexpr auto kMaxDataBits = 256; static constexpr auto kMaxDataSize = 256; static constexpr auto kMaxLength = 140; static constexpr auto kMaxSpectrumHistory = 4; static constexpr auto kMaxRecordedFrames = 1024; - static constexpr auto kDefaultFixedLength = 82; - static constexpr auto kDefaultFixedECCBytes = 32; struct TxProtocol { const char * name; @@ -53,11 +46,6 @@ public: int aSampleSizeBytesOut); ~GGWave(); - void setTxMode(TxMode aTxMode) { - txMode = aTxMode; - init(0, "", getDefultTxProtocol()); - } - bool init(int textLength, const char * stext, const TxProtocol & aProtocol); void send(const CBQueueAudio & cbQueueAudio); void receive(const CBDequeueAudio & CBDequeueAudio); @@ -175,7 +163,6 @@ private: std::string textToSend; - TxMode txMode = TxMode::FixedLength; TxProtocol txProtocol; AmplitudeData outputBlock; diff --git a/src/ggwave.cpp b/src/ggwave.cpp index 4ba6d65..92c56e5 100644 --- a/src/ggwave.cpp +++ b/src/ggwave.cpp @@ -121,7 +121,17 @@ GGWave::GGWave( sampleSizeBytesIn = aSampleSizeBytesIn; sampleSizeBytesOut = aSampleSizeBytesOut; - setTxMode(TxMode::VariableLength); + isamplesPerFrame = 1.0f/samplesPerFrame; + hzPerSample = sampleRateIn/samplesPerFrame; + ihzPerSample = 1.0/hzPerSample; + freqDelta_bin = 1; + freqDelta_hz = 2*hzPerSample; + + nBitsInMarker = 16; + nMarkerFrames = 16; + nPostMarkerFrames = 0; + + init(0, "", getDefultTxProtocol()); } GGWave::~GGWave() { @@ -138,36 +148,69 @@ bool GGWave::init(int textLength, const char * stext, const TxProtocol & aProtoc txProtocol = aProtocol; const uint8_t * text = reinterpret_cast(stext); - frameId = 0; - nIterations = 0; + + nECCBytesPerTx = getECCBytesForLength(textLength); + sendDataLength = textLength + 3; + + if (rsData) delete rsData; + if (rsLength) delete rsLength; + + rsData = new RS::ReedSolomon(textLength, nECCBytesPerTx); + rsLength = new RS::ReedSolomon(1, 2); + hasData = false; + txData.fill(0); + txDataEncoded.fill(0); - isamplesPerFrame = 1.0f/samplesPerFrame; - sendVolume = ((double)(txProtocol.paramVolume))/100.0f; - hzPerSample = sampleRateIn/samplesPerFrame; - ihzPerSample = 1.0/hzPerSample; + if (textLength > 0) { + txData[0] = textLength; + for (int i = 0; i < textLength; ++i) txData[i + 1] = text[i]; + rsLength->Encode(txData.data(), txDataEncoded.data()); + rsData->Encode(txData.data() + 1, txDataEncoded.data() + 3); - nDataBitsPerTx = 8*txProtocol.paramBytesPerTx; - nECCBytesPerTx = (txMode == TxMode::FixedLength) ? kDefaultFixedECCBytes : getECCBytesForLength(textLength); + hasData = true; + } + + // Rx + receivingData = false; + analyzingData = false; framesToAnalyze = 0; framesLeftToAnalyze = 0; framesToRecord = 0; framesLeftToRecord = 0; - nBitsInMarker = 16; - nMarkerFrames = 16; - nPostMarkerFrames = 0; - sendDataLength = (txMode == TxMode::FixedLength) ? kDefaultFixedLength : textLength + 3; - freqDelta_bin = 1; - freqDelta_hz = 2*hzPerSample; + sampleAmplitude.fill(0); + sampleSpectrum.fill(0); + for (auto & s : sampleAmplitudeHistory) { + s.fill(0); + } + + rxData.fill(0); + + for (int i = 0; i < samplesPerFrame; ++i) { + fftOut[i].real(0.0f); + fftOut[i].imag(0.0f); + } + + return true; +} + +void GGWave::send(const CBQueueAudio & cbQueueAudio) { + int samplesPerFrameOut = (sampleRateOut/sampleRateIn)*samplesPerFrame; + if (sampleRateOut != sampleRateIn) { + printf("Resampling from %d Hz to %d Hz\n", (int) sampleRateIn, (int) sampleRateOut); + } + + frameId = 0; + nIterations = 0; + + nDataBitsPerTx = 8*txProtocol.paramBytesPerTx; + sendVolume = ((double)(txProtocol.paramVolume))/100.0f; freqStart_hz = hzPerSample*txProtocol.paramFreqStart; outputBlock.fill(0); - txData.fill(0); - txDataEncoded.fill(0); - for (int k = 0; k < (int) phaseOffsets.size(); ++k) { phaseOffsets[k] = (M_PI*k)/(nDataBitsPerTx); } @@ -195,57 +238,6 @@ bool GGWave::init(int textLength, const char * stext, const TxProtocol & aProtoc } } - if (rsData) delete rsData; - if (rsLength) delete rsLength; - - if (txMode == TxMode::FixedLength) { - rsData = new RS::ReedSolomon(kDefaultFixedLength, kDefaultFixedLength); - rsLength = nullptr; - } else { - rsData = new RS::ReedSolomon(textLength, nECCBytesPerTx); - rsLength = new RS::ReedSolomon(1, 2); - } - - if (textLength > 0) { - if (txMode == TxMode::FixedLength) { - for (int i = 0; i < textLength; ++i) txData[i] = text[i]; - rsData->Encode(txData.data(), txDataEncoded.data()); - } else { - txData[0] = textLength; - for (int i = 0; i < textLength; ++i) txData[i + 1] = text[i]; - rsData->Encode(txData.data() + 1, txDataEncoded.data() + 3); - rsLength->Encode(txData.data(), txDataEncoded.data()); - } - - hasData = true; - } - - // Rx - receivingData = false; - analyzingData = false; - - sampleAmplitude.fill(0); - sampleSpectrum.fill(0); - for (auto & s : sampleAmplitudeHistory) { - s.fill(0); - } - - rxData.fill(0); - - for (int i = 0; i < samplesPerFrame; ++i) { - fftOut[i].real(0.0f); - fftOut[i].imag(0.0f); - } - - return true; -} - -void GGWave::send(const CBQueueAudio & cbQueueAudio) { - int samplesPerFrameOut = (sampleRateOut/sampleRateIn)*samplesPerFrame; - if (sampleRateOut != sampleRateIn) { - printf("Resampling from %d Hz to %d Hz\n", (int) sampleRateIn, (int) sampleRateOut); - } - while (hasData) { int nBytesPerTx = nDataBitsPerTx/8; std::fill(outputBlock.begin(), outputBlock.end(), 0.0f); @@ -320,7 +312,7 @@ void GGWave::send(const CBQueueAudio & cbQueueAudio) { ::addAmplitudeSmooth(bit1Amplitude[k/2], outputBlock, sendVolume, 0, samplesPerFrameOut, cycleModMain, txProtocol.paramFramesPerTx); } } - } else if (txMode == TxMode::VariableLength && frameId < + } else if (frameId < (nMarkerFrames + nPostMarkerFrames) + ((sendDataLength + nECCBytesPerTx)/nBytesPerTx + 2)*txProtocol.paramFramesPerTx + (nMarkerFrames)) { @@ -364,7 +356,6 @@ void GGWave::receive(const CBDequeueAudio & CBDequeueAudio) { // todo : support for non-float input auto nBytesRecorded = CBDequeueAudio(sampleAmplitude.data(), samplesPerFrame*sampleSizeBytesIn); - if (nBytesRecorded != 0) { { sampleAmplitudeHistory[historyId] = sampleAmplitude; @@ -373,7 +364,7 @@ void GGWave::receive(const CBDequeueAudio & CBDequeueAudio) { historyId = 0; } - if (historyId == 0 && (receivingData == false || (receivingData && txMode == TxMode::VariableLength))) { + if (historyId == 0 && (receivingData == false || receivingData)) { std::fill(sampleAmplitudeAverage.begin(), sampleAmplitudeAverage.end(), 0.0f); for (auto & s : sampleAmplitudeHistory) { for (int i = 0; i < samplesPerFrame; ++i) { @@ -423,6 +414,7 @@ void GGWave::receive(const CBDequeueAudio & CBDequeueAudio) { const int stepsPerFrame = 16; const int step = samplesPerFrame/stepsPerFrame; + const int encodedOffset = 3; bool isValid = false; for (const auto & rxProtocol : txProtocols) { @@ -431,11 +423,9 @@ void GGWave::receive(const CBDequeueAudio & CBDequeueAudio) { framesToAnalyze = nMarkerFrames*stepsPerFrame; framesLeftToAnalyze = framesToAnalyze; for (int ii = nMarkerFrames*stepsPerFrame - 1; ii >= nMarkerFrames*stepsPerFrame/2; --ii) { - bool knownLength = txMode == TxMode::FixedLength; + bool knownLength = false; const int offsetStart = ii; - const int encodedOffset = (knownLength) ? 0 : 3; - for (int itx = 0; itx < 1024; ++itx) { int offsetTx = offsetStart + itx*rxProtocol.paramFramesPerTx*stepsPerFrame; if (offsetTx >= recvDuration_frames*stepsPerFrame || (itx + 1)*rxProtocol.paramBytesPerTx >= (int) txDataEncoded.size()) { @@ -484,48 +474,30 @@ void GGWave::receive(const CBDequeueAudio & CBDequeueAudio) { } } - if (txMode == TxMode::VariableLength) { - if (itx*rxProtocol.paramBytesPerTx > 3 && knownLength == false) { - if ((rsLength->Decode(txDataEncoded.data(), rxData.data()) == 0) && (rxData[0] > 0 && rxData[0] <= 140)) { - knownLength = true; - } else { - break; - } + if (itx*rxProtocol.paramBytesPerTx > 3 && knownLength == false) { + if ((rsLength->Decode(txDataEncoded.data(), rxData.data()) == 0) && (rxData[0] > 0 && rxData[0] <= 140)) { + knownLength = true; + } else { + break; } } } - if (txMode == TxMode::VariableLength && knownLength) { + if (knownLength) { if (rsData) delete rsData; rsData = new RS::ReedSolomon(rxData[0], ::getECCBytesForLength(rxData[0])); - } - if (knownLength) { - if (txMode == FixedLength) { - if (rsData->Decode(txDataEncoded.data() + encodedOffset, rxData.data()) == 0) { - if (rxData[0] == 'A') { - printf("[ANSWER] Received sound data successfully!\n"); - } else if (rxData[0] == 'O') { - printf("[OFFER] Received sound data successfully!\n"); - } + int decodedLength = rxData[0]; + if (rsData->Decode(txDataEncoded.data() + encodedOffset, rxData.data()) == 0) { + if (rxData[0] != 0) { + std::string s((char *) rxData.data(), decodedLength); + + printf("Decoded length = %d\n", decodedLength); + printf("Received sound data successfully: '%s'\n", s.c_str()); isValid = true; hasNewRxData = true; - lastRxDataLength = kDefaultFixedLength; - } - } else { - int decodedLength = rxData[0]; - if (rsData->Decode(txDataEncoded.data() + encodedOffset, rxData.data()) == 0) { - if (rxData[0] != 0) { - std::string s((char *) rxData.data(), decodedLength); - - printf("Decoded length = %d\n", decodedLength); - printf("Received sound data successfully: '%s'\n", s.c_str()); - - isValid = true; - hasNewRxData = true; - lastRxDataLength = decodedLength; - } + lastRxDataLength = decodedLength; } } } @@ -585,17 +557,17 @@ void GGWave::receive(const CBDequeueAudio & CBDequeueAudio) { if (isReceiving) { std::time_t timestamp = std::time(nullptr); printf("%sReceiving sound data ...\n", std::asctime(std::localtime(×tamp))); + rxData.fill(0); receivingData = true; - if (txMode == TxMode::FixedLength) { - recvDuration_frames = 2*nMarkerFrames + nPostMarkerFrames + maxFramesPerTx()*((kDefaultFixedLength + kDefaultFixedECCBytes)/minBytesPerTx() + 1); - } else { - recvDuration_frames = 2*nMarkerFrames + nPostMarkerFrames + maxFramesPerTx()*((kMaxLength + ::getECCBytesForLength(kMaxLength))/minBytesPerTx() + 1); - } + + // max recieve duration + recvDuration_frames = 2*nMarkerFrames + nPostMarkerFrames + maxFramesPerTx()*((kMaxLength + ::getECCBytesForLength(kMaxLength))/minBytesPerTx() + 1); + framesToRecord = recvDuration_frames; framesLeftToRecord = recvDuration_frames; } - } else if (txMode == TxMode::VariableLength) { + } else { bool isEnded = false; for (const auto & rxProtocol : txProtocols) { From 4a8a219f39074319d8c9c699880b62338ab1dada Mon Sep 17 00:00:00 2001 From: Georgi Gerganov Date: Sat, 5 Dec 2020 13:20:06 +0200 Subject: [PATCH 07/12] wip : refactor GGWave --- examples/ggwave-common-sdl2.cpp | 7 +- include/ggwave/ggwave.h | 186 ++++++------- src/ggwave.cpp | 465 ++++++++++++++++---------------- 3 files changed, 320 insertions(+), 338 deletions(-) diff --git a/examples/ggwave-common-sdl2.cpp b/examples/ggwave-common-sdl2.cpp index ba4242e..63e7d09 100644 --- a/examples/ggwave-common-sdl2.cpp +++ b/examples/ggwave-common-sdl2.cpp @@ -48,9 +48,6 @@ extern "C" { EMSCRIPTEN_KEEPALIVE int getSampleRate() { return g_ggWave->getSampleRateIn(); } - EMSCRIPTEN_KEEPALIVE - float getAverageRxTime_ms() { return g_ggWave->getAverageRxTime_ms(); } - EMSCRIPTEN_KEEPALIVE int getFramesToRecord() { return g_ggWave->getFramesToRecord(); } @@ -67,7 +64,7 @@ extern "C" { int hasDeviceOutput() { return g_devIdOut; } EMSCRIPTEN_KEEPALIVE - int hasDeviceCapture() { return (g_ggWave->getTotalBytesCaptured() > 0) ? g_devIdIn : 0; } + int hasDeviceCapture() { return g_devIdIn; } EMSCRIPTEN_KEEPALIVE int doInit() { @@ -239,7 +236,7 @@ bool GGWave_mainLoop() { return SDL_DequeueAudio(g_devIdIn, data, nMaxBytes); }; - if (g_ggWave->getHasData() == false) { + if (g_ggWave->hasTxData() == false) { SDL_PauseAudioDevice(g_devIdOut, SDL_FALSE); static auto tLastNoData = std::chrono::high_resolution_clock::now(); diff --git a/include/ggwave/ggwave.h b/include/ggwave/ggwave.h index e1eba88..c856f25 100644 --- a/include/ggwave/ggwave.h +++ b/include/ggwave/ggwave.h @@ -5,6 +5,7 @@ #include #include #include +#include namespace RS { class ReedSolomon; @@ -22,10 +23,12 @@ public: struct TxProtocol { const char * name; - int paramFreqStart; - int paramFramesPerTx; - int paramBytesPerTx; - int paramVolume; + int freqStart; + int framesPerTx; + int bytesPerTx; + int volume; + + int nDataBitsPerTx() const { return 8*bytesPerTx; } }; using AmplitudeData = std::array; @@ -39,142 +42,109 @@ public: using CBDequeueAudio = std::function; GGWave( - int aSampleRateIn, - int aSampleRateOut, - int aSamplesPerFrame, - int aSampleSizeBytesIn, - int aSampleSizeBytesOut); + int sampleRateIn, + int sampleRateOut, + int samplesPerFrame, + int sampleSizeBytesIn, + int sampleSizeBytesOut); ~GGWave(); bool init(int textLength, const char * stext, const TxProtocol & aProtocol); void send(const CBQueueAudio & cbQueueAudio); void receive(const CBDequeueAudio & CBDequeueAudio); - const bool & getHasData() const { return hasData; } + const bool & hasTxData() const { return m_hasNewTxData; } - const int & getFramesToRecord() const { return framesToRecord; } - const int & getFramesLeftToRecord() const { return framesLeftToRecord; } - const int & getFramesToAnalyze() const { return framesToAnalyze; } - const int & getFramesLeftToAnalyze() const { return framesLeftToAnalyze; } - const int & getSamplesPerFrame() const { return samplesPerFrame; } - const int & getSampleSizeBytesIn() const { return sampleSizeBytesIn; } - const int & getSampleSizeBytesOut() const { return sampleSizeBytesOut; } - const int & getTotalBytesCaptured() const { return totalBytesCaptured; } + const int & getFramesToRecord() const { return m_framesToRecord; } + const int & getFramesLeftToRecord() const { return m_framesLeftToRecord; } + const int & getFramesToAnalyze() const { return m_framesToAnalyze; } + const int & getFramesLeftToAnalyze() const { return m_framesLeftToAnalyze; } + const int & getSamplesPerFrame() const { return m_samplesPerFrame; } + const int & getSampleSizeBytesIn() const { return m_sampleSizeBytesIn; } + const int & getSampleSizeBytesOut() const { return m_sampleSizeBytesOut; } - const float & getSampleRateIn() const { return sampleRateIn; } - const float & getAverageRxTime_ms() const { return averageRxTime_ms; } + const float & getSampleRateIn() const { return m_sampleRateIn; } - const TxRxData & getRxData() const { return rxData; } + const TxRxData & getRxData() const { return m_rxData; } const TxProtocol & getDefultTxProtocol() const { return txProtocols[1]; } const TxProtocols & getTxProtocols() const { return txProtocols; } - int takeRxData(TxRxData & dst) { - if (lastRxDataLength == 0) return 0; - - auto res = lastRxDataLength; - lastRxDataLength = 0; - dst = rxData; - - return res; - } + int takeRxData(TxRxData & dst); private: const TxProtocols txProtocols { - { "Normal", 40, 9, 3, 50 }, - { "Fast", 40, 6, 3, 50 }, - { "Fastest", 40, 3, 3, 50 }, - { "Ultrasonic", 320, 9, 3, 50 }, + { "Normal", 40, 9, 3, 50 }, + { "Fast", 40, 6, 3, 50 }, + { "Fastest", 40, 3, 3, 50 }, + { "[U] Normal", 320, 9, 3, 50 }, + { "[U] Fast", 320, 6, 3, 50 }, + { "[U] Fastest", 320, 3, 3, 50 }, }; - int maxFramesPerTx() const { - int res = 0; - for (const auto & protocol : txProtocols) { - res = std::max(res, protocol.paramFramesPerTx); - } - return res; + int maxFramesPerTx() const; + int minBytesPerTx() const; + + double bitFreq(const TxProtocol & p, int bit) const { + return m_hzPerSample*p.freqStart + m_freqDelta_hz*bit; } - int minBytesPerTx() const { - int res = txProtocols.front().paramFramesPerTx; - for (const auto & protocol : txProtocols) { - res = std::min(res, protocol.paramBytesPerTx); - } - return res; - } + const float m_sampleRateIn; + const float m_sampleRateOut; + const int m_samplesPerFrame; + const float m_isamplesPerFrame; + const int m_sampleSizeBytesIn; + const int m_sampleSizeBytesOut; - int nIterations; + const float m_hzPerSample; + const float m_ihzPerSample; + + const int m_freqDelta_bin; + const float m_freqDelta_hz; + + const int m_nBitsInMarker; + const int m_nMarkerFrames; + const int m_nPostMarkerFrames; + const int m_encodedDataOffset; // Rx - bool receivingData; - bool analyzingData; - bool hasNewRxData = false; + bool m_hasNewRxData; + bool m_receivingData; + bool m_analyzingData; - int nCalls = 0; - int recvDuration_frames; - int totalBytesCaptured; - int lastRxDataLength = 0; + int m_markerFreqStart; - float tSum_ms = 0.0f; - float averageRxTime_ms = 0.0; + int m_recvDuration_frames; + int m_lastRxDataLength; - std::array fftIn; - std::array, kMaxSamplesPerFrame> fftOut; + int m_framesLeftToAnalyze; + int m_framesLeftToRecord; + int m_framesToAnalyze; + int m_framesToRecord; - AmplitudeData sampleAmplitude; - SpectrumData sampleSpectrum; + std::array m_fftIn; + std::array, kMaxSamplesPerFrame> m_fftOut; - TxRxData rxData; - TxRxData txData; - TxRxData txDataEncoded; + AmplitudeData m_sampleAmplitude; + SpectrumData m_sampleSpectrum; - int historyId = 0; - AmplitudeData sampleAmplitudeAverage; - std::array sampleAmplitudeHistory; + TxRxData m_rxData; - RecordedData recordedAmplitude; + int m_historyId = 0; + AmplitudeData m_sampleAmplitudeAverage; + std::array m_sampleAmplitudeHistory; + + RecordedData m_recordedAmplitude; // Tx - bool hasData; + bool m_hasNewTxData; + int m_nECCBytesPerTx; + int m_sendDataLength; - float freqDelta_hz; - float freqStart_hz; - float hzPerSample; - float ihzPerSample; - float isamplesPerFrame; - float sampleRateIn; - float sampleRateOut; - float sendVolume; + int m_txDataLength; + TxRxData m_txData; + TxRxData m_txDataEncoded; - int frameId; - int framesLeftToAnalyze; - int framesLeftToRecord; - int framesToAnalyze; - int framesToRecord; - int freqDelta_bin = 1; - int nBitsInMarker; - int nDataBitsPerTx; - int nECCBytesPerTx; - int nMarkerFrames; - int nPostMarkerFrames; - int sampleSizeBytesIn; - int sampleSizeBytesOut; - int samplesPerFrame; - int sendDataLength; + TxProtocol m_txProtocol; - std::string textToSend; - - TxProtocol txProtocol; - - AmplitudeData outputBlock; - AmplitudeData16 outputBlock16; - - std::array dataBits; - std::array phaseOffsets; - std::array dataFreqs_hz; - - std::array bit1Amplitude; - std::array bit0Amplitude; - - RS::ReedSolomon * rsData = nullptr; - RS::ReedSolomon * rsLength = nullptr; + std::unique_ptr m_rsLength; }; diff --git a/src/ggwave.cpp b/src/ggwave.cpp index 92c56e5..ec36319 100644 --- a/src/ggwave.cpp +++ b/src/ggwave.cpp @@ -109,34 +109,31 @@ int getECCBytesForLength(int len) { } GGWave::GGWave( - int aSampleRateIn, - int aSampleRateOut, - int aSamplesPerFrame, - int aSampleSizeBytesIn, - int aSampleSizeBytesOut) { - - sampleRateIn = aSampleRateIn; - sampleRateOut = aSampleRateOut; - samplesPerFrame = aSamplesPerFrame; - sampleSizeBytesIn = aSampleSizeBytesIn; - sampleSizeBytesOut = aSampleSizeBytesOut; - - isamplesPerFrame = 1.0f/samplesPerFrame; - hzPerSample = sampleRateIn/samplesPerFrame; - ihzPerSample = 1.0/hzPerSample; - freqDelta_bin = 1; - freqDelta_hz = 2*hzPerSample; - - nBitsInMarker = 16; - nMarkerFrames = 16; - nPostMarkerFrames = 0; - + int sampleRateIn, + int sampleRateOut, + int samplesPerFrame, + int sampleSizeBytesIn, + int sampleSizeBytesOut) : + m_sampleRateIn(sampleRateIn), + m_sampleRateOut(sampleRateOut), + m_samplesPerFrame(samplesPerFrame), + m_isamplesPerFrame(1.0f/m_samplesPerFrame), + m_sampleSizeBytesIn(sampleSizeBytesIn), + m_sampleSizeBytesOut(sampleSizeBytesOut), + m_hzPerSample(m_sampleRateIn/samplesPerFrame), + m_ihzPerSample(1.0f/m_hzPerSample), + m_freqDelta_bin(1), + m_freqDelta_hz(2*m_hzPerSample), + m_nBitsInMarker(16), + m_nMarkerFrames(16), + m_nPostMarkerFrames(0), + m_encodedDataOffset(3), + m_rsLength(new RS::ReedSolomon(1, m_encodedDataOffset - 1)) +{ init(0, "", getDefultTxProtocol()); } GGWave::~GGWave() { - if (rsData) delete rsData; - if (rsLength) delete rsLength; } bool GGWave::init(int textLength, const char * stext, const TxProtocol & aProtocol) { @@ -145,74 +142,64 @@ bool GGWave::init(int textLength, const char * stext, const TxProtocol & aProtoc textLength = kMaxLength; } - txProtocol = aProtocol; + m_txProtocol = aProtocol; + m_txDataLength = textLength; const uint8_t * text = reinterpret_cast(stext); - nECCBytesPerTx = getECCBytesForLength(textLength); - sendDataLength = textLength + 3; + m_hasNewTxData = false; + m_txData.fill(0); + m_txDataEncoded.fill(0); - if (rsData) delete rsData; - if (rsLength) delete rsLength; + if (m_txDataLength > 0) { + m_txData[0] = m_txDataLength; + for (int i = 0; i < m_txDataLength; ++i) m_txData[i + 1] = text[i]; - rsData = new RS::ReedSolomon(textLength, nECCBytesPerTx); - rsLength = new RS::ReedSolomon(1, 2); - - hasData = false; - txData.fill(0); - txDataEncoded.fill(0); - - if (textLength > 0) { - txData[0] = textLength; - for (int i = 0; i < textLength; ++i) txData[i + 1] = text[i]; - rsLength->Encode(txData.data(), txDataEncoded.data()); - rsData->Encode(txData.data() + 1, txDataEncoded.data() + 3); - - hasData = true; + m_hasNewTxData = true; } // Rx - receivingData = false; - analyzingData = false; + m_receivingData = false; + m_analyzingData = false; - framesToAnalyze = 0; - framesLeftToAnalyze = 0; - framesToRecord = 0; - framesLeftToRecord = 0; + m_framesToAnalyze = 0; + m_framesLeftToAnalyze = 0; + m_framesToRecord = 0; + m_framesLeftToRecord = 0; - sampleAmplitude.fill(0); - sampleSpectrum.fill(0); - for (auto & s : sampleAmplitudeHistory) { + m_sampleAmplitude.fill(0); + m_sampleSpectrum.fill(0); + for (auto & s : m_sampleAmplitudeHistory) { s.fill(0); } - rxData.fill(0); + m_rxData.fill(0); - for (int i = 0; i < samplesPerFrame; ++i) { - fftOut[i].real(0.0f); - fftOut[i].imag(0.0f); + for (int i = 0; i < m_samplesPerFrame; ++i) { + m_fftOut[i].real(0.0f); + m_fftOut[i].imag(0.0f); } return true; } void GGWave::send(const CBQueueAudio & cbQueueAudio) { - int samplesPerFrameOut = (sampleRateOut/sampleRateIn)*samplesPerFrame; - if (sampleRateOut != sampleRateIn) { - printf("Resampling from %d Hz to %d Hz\n", (int) sampleRateIn, (int) sampleRateOut); + int samplesPerFrameOut = (m_sampleRateOut/m_sampleRateIn)*m_samplesPerFrame; + if (m_sampleRateOut != m_sampleRateIn) { + printf("Resampling from %d Hz to %d Hz\n", (int) m_sampleRateIn, (int) m_sampleRateOut); } - frameId = 0; - nIterations = 0; + int frameId = 0; - nDataBitsPerTx = 8*txProtocol.paramBytesPerTx; - sendVolume = ((double)(txProtocol.paramVolume))/100.0f; - freqStart_hz = hzPerSample*txProtocol.paramFreqStart; + float sendVolume = ((double)(m_txProtocol.volume))/100.0f; - outputBlock.fill(0); + AmplitudeData outputBlock; + AmplitudeData16 outputBlock16; + + std::array phaseOffsets; for (int k = 0; k < (int) phaseOffsets.size(); ++k) { - phaseOffsets[k] = (M_PI*k)/(nDataBitsPerTx); + phaseOffsets[k] = (M_PI*k)/(m_txProtocol.nDataBitsPerTx()); } // note : what is the purpose of this shuffle ? I forgot .. :( @@ -221,114 +208,124 @@ void GGWave::send(const CBQueueAudio & cbQueueAudio) { std::shuffle(phaseOffsets.begin(), phaseOffsets.end(), g); + std::array dataBits; + + std::array bit1Amplitude; + std::array bit0Amplitude; + for (int k = 0; k < (int) dataBits.size(); ++k) { - double freq = freqStart_hz + freqDelta_hz*k; - dataFreqs_hz[k] = freq; + double freq = bitFreq(m_txProtocol, k); double phaseOffset = phaseOffsets[k]; - double curHzPerFrame = sampleRateOut/samplesPerFrame; - double curIHzPerFrame = 1.0/curHzPerFrame; - for (int i = 0; i < samplesPerFrame; i++) { + double curHzPerSample = m_sampleRateOut/m_samplesPerFrame; + double curIHzPerSample = 1.0/curHzPerSample; + for (int i = 0; i < m_samplesPerFrame; i++) { double curi = i; - bit1Amplitude[k][i] = std::sin((2.0*M_PI)*(curi*isamplesPerFrame)*(freq*curIHzPerFrame) + phaseOffset); + bit1Amplitude[k][i] = std::sin((2.0*M_PI)*(curi*m_isamplesPerFrame)*(freq*curIHzPerSample) + phaseOffset); } - for (int i = 0; i < samplesPerFrame; i++) { + for (int i = 0; i < m_samplesPerFrame; i++) { double curi = i; - bit0Amplitude[k][i] = std::sin((2.0*M_PI)*(curi*isamplesPerFrame)*((freq + hzPerSample*freqDelta_bin)*curIHzPerFrame) + phaseOffset); + bit0Amplitude[k][i] = std::sin((2.0*M_PI)*(curi*m_isamplesPerFrame)*((freq + m_hzPerSample*m_freqDelta_bin)*curIHzPerSample) + phaseOffset); } } - while (hasData) { - int nBytesPerTx = nDataBitsPerTx/8; - std::fill(outputBlock.begin(), outputBlock.end(), 0.0f); - std::uint16_t nFreq = 0; + m_nECCBytesPerTx = getECCBytesForLength(m_txDataLength); + m_sendDataLength = m_txDataLength + m_encodedDataOffset; - if (sampleRateOut != sampleRateIn) { - for (int k = 0; k < nDataBitsPerTx; ++k) { - double freq = freqStart_hz + freqDelta_hz*k; + RS::ReedSolomon rsData = RS::ReedSolomon(m_txDataLength, m_nECCBytesPerTx); + + m_rsLength->Encode(m_txData.data(), m_txDataEncoded.data()); + rsData.Encode(m_txData.data() + 1, m_txDataEncoded.data() + m_encodedDataOffset); + + while (m_hasNewTxData) { + std::fill(outputBlock.begin(), outputBlock.end(), 0.0f); + + if (m_sampleRateOut != m_sampleRateIn) { + for (int k = 0; k < m_txProtocol.nDataBitsPerTx(); ++k) { + double freq = bitFreq(m_txProtocol, k); double phaseOffset = phaseOffsets[k]; - double curHzPerFrame = sampleRateOut/samplesPerFrame; - double curIHzPerFrame = 1.0/curHzPerFrame; + double curHzPerSample = m_sampleRateOut/m_samplesPerFrame; + double curIHzPerSample = 1.0/curHzPerSample; for (int i = 0; i < samplesPerFrameOut; i++) { double curi = (i + frameId*samplesPerFrameOut); - bit1Amplitude[k][i] = std::sin((2.0*M_PI)*(curi*isamplesPerFrame)*(freq*curIHzPerFrame) + phaseOffset); + bit1Amplitude[k][i] = std::sin((2.0*M_PI)*(curi*m_isamplesPerFrame)*(freq*curIHzPerSample) + phaseOffset); } for (int i = 0; i < samplesPerFrameOut; i++) { double curi = (i + frameId*samplesPerFrameOut); - bit0Amplitude[k][i] = std::sin((2.0*M_PI)*(curi*isamplesPerFrame)*((freq + hzPerSample*freqDelta_bin)*curIHzPerFrame) + phaseOffset); + bit0Amplitude[k][i] = std::sin((2.0*M_PI)*(curi*m_isamplesPerFrame)*((freq + m_hzPerSample*m_freqDelta_bin)*curIHzPerSample) + phaseOffset); } } } - if (frameId < nMarkerFrames) { - nFreq = nBitsInMarker; + std::uint16_t nFreq = 0; + if (frameId < m_nMarkerFrames) { + nFreq = m_nBitsInMarker; - for (int i = 0; i < nBitsInMarker; ++i) { + for (int i = 0; i < m_nBitsInMarker; ++i) { if (i%2 == 0) { - ::addAmplitudeSmooth(bit1Amplitude[i], outputBlock, sendVolume, 0, samplesPerFrameOut, frameId, nMarkerFrames); + ::addAmplitudeSmooth(bit1Amplitude[i], outputBlock, sendVolume, 0, samplesPerFrameOut, frameId, m_nMarkerFrames); } else { - ::addAmplitudeSmooth(bit0Amplitude[i], outputBlock, sendVolume, 0, samplesPerFrameOut, frameId, nMarkerFrames); + ::addAmplitudeSmooth(bit0Amplitude[i], outputBlock, sendVolume, 0, samplesPerFrameOut, frameId, m_nMarkerFrames); } } - } else if (frameId < nMarkerFrames + nPostMarkerFrames) { - nFreq = nBitsInMarker; + } else if (frameId < m_nMarkerFrames + m_nPostMarkerFrames) { + nFreq = m_nBitsInMarker; - for (int i = 0; i < nBitsInMarker; ++i) { + for (int i = 0; i < m_nBitsInMarker; ++i) { if (i%2 == 0) { - ::addAmplitudeSmooth(bit0Amplitude[i], outputBlock, sendVolume, 0, samplesPerFrameOut, frameId - nMarkerFrames, nPostMarkerFrames); + ::addAmplitudeSmooth(bit0Amplitude[i], outputBlock, sendVolume, 0, samplesPerFrameOut, frameId - m_nMarkerFrames, m_nPostMarkerFrames); } else { - ::addAmplitudeSmooth(bit1Amplitude[i], outputBlock, sendVolume, 0, samplesPerFrameOut, frameId - nMarkerFrames, nPostMarkerFrames); + ::addAmplitudeSmooth(bit1Amplitude[i], outputBlock, sendVolume, 0, samplesPerFrameOut, frameId - m_nMarkerFrames, m_nPostMarkerFrames); } } } else if (frameId < - (nMarkerFrames + nPostMarkerFrames) + - ((sendDataLength + nECCBytesPerTx)/nBytesPerTx + 2)*txProtocol.paramFramesPerTx) { - int dataOffset = frameId - nMarkerFrames - nPostMarkerFrames; - int cycleModMain = dataOffset%txProtocol.paramFramesPerTx; - dataOffset /= txProtocol.paramFramesPerTx; - dataOffset *= nBytesPerTx; + (m_nMarkerFrames + m_nPostMarkerFrames) + + ((m_sendDataLength + m_nECCBytesPerTx)/m_txProtocol.bytesPerTx + 2)*m_txProtocol.framesPerTx) { + int dataOffset = frameId - m_nMarkerFrames - m_nPostMarkerFrames; + int cycleModMain = dataOffset%m_txProtocol.framesPerTx; + dataOffset /= m_txProtocol.framesPerTx; + dataOffset *= m_txProtocol.bytesPerTx; dataBits.fill(0); - for (int j = 0; j < nBytesPerTx; ++j) { + for (int j = 0; j < m_txProtocol.bytesPerTx; ++j) { { - uint8_t d = txDataEncoded[dataOffset + j] & 15; + uint8_t d = m_txDataEncoded[dataOffset + j] & 15; dataBits[(2*j + 0)*16 + d] = 1; } { - uint8_t d = txDataEncoded[dataOffset + j] & 240; + uint8_t d = m_txDataEncoded[dataOffset + j] & 240; dataBits[(2*j + 1)*16 + (d >> 4)] = 1; } } - for (int k = 0; k < 2*nBytesPerTx*16; ++k) { + for (int k = 0; k < 2*m_txProtocol.bytesPerTx*16; ++k) { if (dataBits[k] == 0) continue; ++nFreq; if (k%2) { - ::addAmplitudeSmooth(bit0Amplitude[k/2], outputBlock, sendVolume, 0, samplesPerFrameOut, cycleModMain, txProtocol.paramFramesPerTx); + ::addAmplitudeSmooth(bit0Amplitude[k/2], outputBlock, sendVolume, 0, samplesPerFrameOut, cycleModMain, m_txProtocol.framesPerTx); } else { - ::addAmplitudeSmooth(bit1Amplitude[k/2], outputBlock, sendVolume, 0, samplesPerFrameOut, cycleModMain, txProtocol.paramFramesPerTx); + ::addAmplitudeSmooth(bit1Amplitude[k/2], outputBlock, sendVolume, 0, samplesPerFrameOut, cycleModMain, m_txProtocol.framesPerTx); } } } else if (frameId < - (nMarkerFrames + nPostMarkerFrames) + - ((sendDataLength + nECCBytesPerTx)/nBytesPerTx + 2)*txProtocol.paramFramesPerTx + - (nMarkerFrames)) { - nFreq = nBitsInMarker; + (m_nMarkerFrames + m_nPostMarkerFrames) + + ((m_sendDataLength + m_nECCBytesPerTx)/m_txProtocol.bytesPerTx + 2)*m_txProtocol.framesPerTx + + (m_nMarkerFrames)) { + nFreq = m_nBitsInMarker; - int fId = frameId - ((nMarkerFrames + nPostMarkerFrames) + ((sendDataLength + nECCBytesPerTx)/nBytesPerTx + 2)*txProtocol.paramFramesPerTx); - for (int i = 0; i < nBitsInMarker; ++i) { + 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, sendVolume, 0, samplesPerFrameOut, fId, nMarkerFrames); + addAmplitudeSmooth(bit0Amplitude[i], outputBlock, sendVolume, 0, samplesPerFrameOut, fId, m_nMarkerFrames); } else { - addAmplitudeSmooth(bit1Amplitude[i], outputBlock, sendVolume, 0, samplesPerFrameOut, fId, nMarkerFrames); + addAmplitudeSmooth(bit1Amplitude[i], outputBlock, sendVolume, 0, samplesPerFrameOut, fId, m_nMarkerFrames); } } } else { - textToSend = ""; - hasData = false; + m_hasNewTxData = false; } if (nFreq == 0) nFreq = 1; @@ -344,138 +341,137 @@ void GGWave::send(const CBQueueAudio & cbQueueAudio) { ++frameId; } - cbQueueAudio(outputBlock16.data(), frameId*samplesPerFrameOut*sampleSizeBytesOut); + + cbQueueAudio(outputBlock16.data(), frameId*samplesPerFrameOut*m_sampleSizeBytesOut); } void GGWave::receive(const CBDequeueAudio & CBDequeueAudio) { - auto tCallStart = std::chrono::high_resolution_clock::now(); - - while (hasData == false) { + while (m_hasNewTxData == false) { // read capture data // // todo : support for non-float input - auto nBytesRecorded = CBDequeueAudio(sampleAmplitude.data(), samplesPerFrame*sampleSizeBytesIn); + auto nBytesRecorded = CBDequeueAudio(m_sampleAmplitude.data(), m_samplesPerFrame*m_sampleSizeBytesIn); if (nBytesRecorded != 0) { { - sampleAmplitudeHistory[historyId] = sampleAmplitude; + m_sampleAmplitudeHistory[m_historyId] = m_sampleAmplitude; - if (++historyId >= kMaxSpectrumHistory) { - historyId = 0; + if (++m_historyId >= kMaxSpectrumHistory) { + m_historyId = 0; } - if (historyId == 0 && (receivingData == false || receivingData)) { - std::fill(sampleAmplitudeAverage.begin(), sampleAmplitudeAverage.end(), 0.0f); - for (auto & s : sampleAmplitudeHistory) { - for (int i = 0; i < samplesPerFrame; ++i) { - sampleAmplitudeAverage[i] += s[i]; + if (m_historyId == 0 && (m_receivingData == false || m_receivingData)) { + std::fill(m_sampleAmplitudeAverage.begin(), m_sampleAmplitudeAverage.end(), 0.0f); + for (auto & s : m_sampleAmplitudeHistory) { + for (int i = 0; i < m_samplesPerFrame; ++i) { + m_sampleAmplitudeAverage[i] += s[i]; } } float norm = 1.0f/kMaxSpectrumHistory; - for (int i = 0; i < samplesPerFrame; ++i) { - sampleAmplitudeAverage[i] *= norm; + for (int i = 0; i < m_samplesPerFrame; ++i) { + m_sampleAmplitudeAverage[i] *= norm; } // calculate spectrum - std::copy(sampleAmplitudeAverage.begin(), sampleAmplitudeAverage.begin() + samplesPerFrame, fftIn.data()); + std::copy(m_sampleAmplitudeAverage.begin(), m_sampleAmplitudeAverage.begin() + m_samplesPerFrame, m_fftIn.data()); - FFT(fftIn.data(), fftOut.data(), samplesPerFrame, 1.0); + FFT(m_fftIn.data(), m_fftOut.data(), m_samplesPerFrame, 1.0); double fsum = 0.0; - for (int i = 0; i < samplesPerFrame; ++i) { - sampleSpectrum[i] = (fftOut[i].real()*fftOut[i].real() + fftOut[i].imag()*fftOut[i].imag()); - fsum += sampleSpectrum[i]; + for (int i = 0; i < m_samplesPerFrame; ++i) { + m_sampleSpectrum[i] = (m_fftOut[i].real()*m_fftOut[i].real() + m_fftOut[i].imag()*m_fftOut[i].imag()); + fsum += m_sampleSpectrum[i]; } - for (int i = 1; i < samplesPerFrame/2; ++i) { - sampleSpectrum[i] += sampleSpectrum[samplesPerFrame - i]; - } - - if (fsum < 1e-10) { - totalBytesCaptured = 0; - } else { - totalBytesCaptured += nBytesRecorded; + for (int i = 1; i < m_samplesPerFrame/2; ++i) { + m_sampleSpectrum[i] += m_sampleSpectrum[m_samplesPerFrame - i]; } } - if (framesLeftToRecord > 0) { - std::copy(sampleAmplitude.begin(), - sampleAmplitude.begin() + samplesPerFrame, - recordedAmplitude.data() + (framesToRecord - framesLeftToRecord)*samplesPerFrame); + if (m_framesLeftToRecord > 0) { + std::copy(m_sampleAmplitude.begin(), + m_sampleAmplitude.begin() + m_samplesPerFrame, + m_recordedAmplitude.data() + (m_framesToRecord - m_framesLeftToRecord)*m_samplesPerFrame); - if (--framesLeftToRecord <= 0) { - analyzingData = true; + if (--m_framesLeftToRecord <= 0) { + m_analyzingData = true; } } } - if (analyzingData) { + if (m_analyzingData) { printf("Analyzing captured data ..\n"); auto tStart = std::chrono::high_resolution_clock::now(); const int stepsPerFrame = 16; - const int step = samplesPerFrame/stepsPerFrame; - const int encodedOffset = 3; + const int step = m_samplesPerFrame/stepsPerFrame; + + std::unique_ptr rsData; bool isValid = false; for (const auto & rxProtocol : txProtocols) { - std::fill(sampleSpectrum.begin(), sampleSpectrum.end(), 0.0f); + // skip Rx protocol if start frequency is different from detected one + if (rxProtocol.freqStart != m_markerFreqStart) { + continue; + } - framesToAnalyze = nMarkerFrames*stepsPerFrame; - framesLeftToAnalyze = framesToAnalyze; - for (int ii = nMarkerFrames*stepsPerFrame - 1; ii >= nMarkerFrames*stepsPerFrame/2; --ii) { + std::fill(m_sampleSpectrum.begin(), m_sampleSpectrum.end(), 0.0f); + + m_framesToAnalyze = m_nMarkerFrames*stepsPerFrame; + m_framesLeftToAnalyze = m_framesToAnalyze; + for (int ii = m_nMarkerFrames*stepsPerFrame - 1; ii >= m_nMarkerFrames*stepsPerFrame/2; --ii) { bool knownLength = false; const int offsetStart = ii; for (int itx = 0; itx < 1024; ++itx) { - int offsetTx = offsetStart + itx*rxProtocol.paramFramesPerTx*stepsPerFrame; - if (offsetTx >= recvDuration_frames*stepsPerFrame || (itx + 1)*rxProtocol.paramBytesPerTx >= (int) txDataEncoded.size()) { + int offsetTx = offsetStart + itx*rxProtocol.framesPerTx*stepsPerFrame; + if (offsetTx >= m_recvDuration_frames*stepsPerFrame || (itx + 1)*rxProtocol.bytesPerTx >= (int) m_txDataEncoded.size()) { break; } std::copy( - recordedAmplitude.begin() + offsetTx*step, - recordedAmplitude.begin() + offsetTx*step + samplesPerFrame, fftIn.data()); + m_recordedAmplitude.begin() + offsetTx*step, + m_recordedAmplitude.begin() + offsetTx*step + m_samplesPerFrame, m_fftIn.data()); - for (int k = 1; k < rxProtocol.paramFramesPerTx - 1; ++k) { - for (int i = 0; i < samplesPerFrame; ++i) { - fftIn[i] += recordedAmplitude[(offsetTx + k*stepsPerFrame)*step + i]; + for (int k = 1; k < rxProtocol.framesPerTx - 1; ++k) { + for (int i = 0; i < m_samplesPerFrame; ++i) { + m_fftIn[i] += m_recordedAmplitude[(offsetTx + k*stepsPerFrame)*step + i]; } } - FFT(fftIn.data(), fftOut.data(), samplesPerFrame, 1.0); + FFT(m_fftIn.data(), m_fftOut.data(), m_samplesPerFrame, 1.0); - for (int i = 0; i < samplesPerFrame; ++i) { - sampleSpectrum[i] = (fftOut[i].real()*fftOut[i].real() + fftOut[i].imag()*fftOut[i].imag()); + for (int i = 0; i < m_samplesPerFrame; ++i) { + m_sampleSpectrum[i] = (m_fftOut[i].real()*m_fftOut[i].real() + m_fftOut[i].imag()*m_fftOut[i].imag()); } - for (int i = 1; i < samplesPerFrame/2; ++i) { - sampleSpectrum[i] += sampleSpectrum[samplesPerFrame - i]; + for (int i = 1; i < m_samplesPerFrame/2; ++i) { + m_sampleSpectrum[i] += m_sampleSpectrum[m_samplesPerFrame - i]; } uint8_t curByte = 0; - for (int i = 0; i < 2*rxProtocol.paramBytesPerTx; ++i) { - double freq = hzPerSample*rxProtocol.paramFreqStart; - int bin = std::round(freq*ihzPerSample) + i*16; + for (int i = 0; i < 2*rxProtocol.bytesPerTx; ++i) { + double freq = m_hzPerSample*rxProtocol.freqStart; + int bin = std::round(freq*m_ihzPerSample) + i*16; int kmax = 0; double amax = 0.0; for (int k = 0; k < 16; ++k) { - if (sampleSpectrum[bin + k] > amax) { + if (m_sampleSpectrum[bin + k] > amax) { kmax = k; - amax = sampleSpectrum[bin + k]; + amax = m_sampleSpectrum[bin + k]; } } if (i%2) { curByte += (kmax << 4); - txDataEncoded[itx*rxProtocol.paramBytesPerTx + i/2] = curByte; + m_txDataEncoded[itx*rxProtocol.bytesPerTx + i/2] = curByte; curByte = 0; } else { curByte = kmax; } } - if (itx*rxProtocol.paramBytesPerTx > 3 && knownLength == false) { - if ((rsLength->Decode(txDataEncoded.data(), rxData.data()) == 0) && (rxData[0] > 0 && rxData[0] <= 140)) { + if (itx*rxProtocol.bytesPerTx > m_encodedDataOffset && knownLength == false) { + if ((m_rsLength->Decode(m_txDataEncoded.data(), m_rxData.data()) == 0) && (m_rxData[0] > 0 && m_rxData[0] <= 140)) { knownLength = true; } else { break; @@ -484,20 +480,19 @@ void GGWave::receive(const CBDequeueAudio & CBDequeueAudio) { } if (knownLength) { - if (rsData) delete rsData; - rsData = new RS::ReedSolomon(rxData[0], ::getECCBytesForLength(rxData[0])); + rsData.reset(new RS::ReedSolomon(m_rxData[0], ::getECCBytesForLength(m_rxData[0]))); - int decodedLength = rxData[0]; - if (rsData->Decode(txDataEncoded.data() + encodedOffset, rxData.data()) == 0) { - if (rxData[0] != 0) { - std::string s((char *) rxData.data(), decodedLength); + int decodedLength = m_rxData[0]; + if (rsData->Decode(m_txDataEncoded.data() + m_encodedDataOffset, m_rxData.data()) == 0) { + if (m_rxData[0] != 0) { + std::string s((char *) m_rxData.data(), decodedLength); printf("Decoded length = %d\n", decodedLength); printf("Received sound data successfully: '%s'\n", s.c_str()); isValid = true; - hasNewRxData = true; - lastRxDataLength = decodedLength; + m_hasNewRxData = true; + m_lastRxDataLength = decodedLength; } } } @@ -505,50 +500,51 @@ void GGWave::receive(const CBDequeueAudio & CBDequeueAudio) { if (isValid) { break; } - --framesLeftToAnalyze; + --m_framesLeftToAnalyze; } if (isValid) break; } - framesToRecord = 0; + m_framesToRecord = 0; if (isValid == false) { printf("Failed to capture sound data. Please try again\n"); - framesToRecord = -1; + m_framesToRecord = -1; } - receivingData = false; - analyzingData = false; + m_receivingData = false; + m_analyzingData = false; - std::fill(sampleSpectrum.begin(), sampleSpectrum.end(), 0.0f); + std::fill(m_sampleSpectrum.begin(), m_sampleSpectrum.end(), 0.0f); - framesToAnalyze = 0; - framesLeftToAnalyze = 0; + m_framesToAnalyze = 0; + m_framesLeftToAnalyze = 0; auto tEnd = std::chrono::high_resolution_clock::now(); - printf("Time to analyze: %d ms\n", (int) std::chrono::duration_cast(tEnd - tStart).count()); + printf("Time to analyze: %g ms\n", getTime_ms(tStart, tEnd)); } // check if receiving data - if (receivingData == false) { + if (m_receivingData == false) { bool isReceiving = false; for (const auto & rxProtocol : txProtocols) { bool isReceivingCur = true; - for (int i = 0; i < nBitsInMarker; ++i) { - double freq = hzPerSample*rxProtocol.paramFreqStart + freqDelta_hz*i; - int bin = std::round(freq*ihzPerSample); + for (int i = 0; i < m_nBitsInMarker; ++i) { + double freq = bitFreq(rxProtocol, i); + int bin = std::round(freq*m_ihzPerSample); if (i%2 == 0) { - if (sampleSpectrum[bin] <= 3.0f*sampleSpectrum[bin + freqDelta_bin]) isReceivingCur = false; + if (m_sampleSpectrum[bin] <= 3.0f*m_sampleSpectrum[bin + m_freqDelta_bin]) isReceivingCur = false; } else { - if (sampleSpectrum[bin] >= 3.0f*sampleSpectrum[bin + freqDelta_bin]) isReceivingCur = false; + if (m_sampleSpectrum[bin] >= 3.0f*m_sampleSpectrum[bin + m_freqDelta_bin]) isReceivingCur = false; } } if (isReceivingCur) { + m_markerFreqStart = rxProtocol.freqStart; isReceiving = true; break; } @@ -558,14 +554,16 @@ void GGWave::receive(const CBDequeueAudio & CBDequeueAudio) { std::time_t timestamp = std::time(nullptr); printf("%sReceiving sound data ...\n", std::asctime(std::localtime(×tamp))); - rxData.fill(0); - receivingData = true; + m_rxData.fill(0); + m_receivingData = true; // max recieve duration - recvDuration_frames = 2*nMarkerFrames + nPostMarkerFrames + maxFramesPerTx()*((kMaxLength + ::getECCBytesForLength(kMaxLength))/minBytesPerTx() + 1); + m_recvDuration_frames = + 2*m_nMarkerFrames + m_nPostMarkerFrames + + maxFramesPerTx()*((kMaxLength + ::getECCBytesForLength(kMaxLength))/minBytesPerTx() + 1); - framesToRecord = recvDuration_frames; - framesLeftToRecord = recvDuration_frames; + m_framesToRecord = m_recvDuration_frames; + m_framesLeftToRecord = m_recvDuration_frames; } } else { bool isEnded = false; @@ -573,14 +571,14 @@ void GGWave::receive(const CBDequeueAudio & CBDequeueAudio) { for (const auto & rxProtocol : txProtocols) { bool isEndedCur = true; - for (int i = 0; i < nBitsInMarker; ++i) { - double freq = hzPerSample*rxProtocol.paramFreqStart + freqDelta_hz*i; - int bin = std::round(freq*ihzPerSample); + for (int i = 0; i < m_nBitsInMarker; ++i) { + double freq = bitFreq(rxProtocol, i); + int bin = std::round(freq*m_ihzPerSample); if (i%2 == 0) { - if (sampleSpectrum[bin] >= 3.0f*sampleSpectrum[bin + freqDelta_bin]) isEndedCur = false; + if (m_sampleSpectrum[bin] >= 3.0f*m_sampleSpectrum[bin + m_freqDelta_bin]) isEndedCur = false; } else { - if (sampleSpectrum[bin] <= 3.0f*sampleSpectrum[bin + freqDelta_bin]) isEndedCur = false; + if (m_sampleSpectrum[bin] <= 3.0f*m_sampleSpectrum[bin + m_freqDelta_bin]) isEndedCur = false; } } @@ -590,25 +588,42 @@ void GGWave::receive(const CBDequeueAudio & CBDequeueAudio) { } } - if (isEnded && framesToRecord > 1) { + if (isEnded && m_framesToRecord > 1) { std::time_t timestamp = std::time(nullptr); - printf("%sReceived end marker. Frames left = %d\n", std::asctime(std::localtime(×tamp)), framesLeftToRecord); - recvDuration_frames -= framesLeftToRecord - 1; - framesLeftToRecord = 1; + printf("%sReceived end marker. Frames left = %d\n", std::asctime(std::localtime(×tamp)), m_framesLeftToRecord); + m_recvDuration_frames -= m_framesLeftToRecord - 1; + m_framesLeftToRecord = 1; } } } else { break; } - - ++nIterations; - } - - auto tCallEnd = std::chrono::high_resolution_clock::now(); - tSum_ms += getTime_ms(tCallStart, tCallEnd); - if (++nCalls == 10) { - averageRxTime_ms = tSum_ms/nCalls; - tSum_ms = 0.0f; - nCalls = 0; } } + +int GGWave::takeRxData(TxRxData & dst) { + if (m_lastRxDataLength == 0) return 0; + + auto res = m_lastRxDataLength; + m_lastRxDataLength = 0; + dst = m_rxData; + + return res; +} + +int GGWave::maxFramesPerTx() const { + int res = 0; + for (const auto & protocol : txProtocols) { + res = std::max(res, protocol.framesPerTx); + } + return res; +} + +int GGWave::minBytesPerTx() const { + int res = txProtocols.front().framesPerTx; + for (const auto & protocol : txProtocols) { + res = std::min(res, protocol.bytesPerTx); + } + return res; +} + From 72d0ca630db0daf6636bf85ee5d4ceab3a01970a Mon Sep 17 00:00:00 2001 From: Georgi Gerganov Date: Sat, 5 Dec 2020 13:32:39 +0200 Subject: [PATCH 08/12] wip : volume no longer protocol parameter --- examples/ggwave-cli/main.cpp | 2 +- examples/ggwave-common-sdl2.cpp | 4 ++-- examples/ggwave-gui/main.cpp | 2 +- include/ggwave/ggwave.h | 33 ++++++++++++++++--------------- src/ggwave.cpp | 35 ++++++++++++++++----------------- 5 files changed, 38 insertions(+), 38 deletions(-) diff --git a/examples/ggwave-cli/main.cpp b/examples/ggwave-cli/main.cpp index bd53cc5..8ad03dc 100644 --- a/examples/ggwave-cli/main.cpp +++ b/examples/ggwave-cli/main.cpp @@ -50,7 +50,7 @@ int main(int argc, char** argv) { } { std::lock_guard lock(mutex); - ggWave->init(input.size(), input.data(), ggWave->getTxProtocols()[txProtocol]); + ggWave->init(input.size(), input.data(), ggWave->getTxProtocols()[txProtocol], 50); } inputOld = input; } diff --git a/examples/ggwave-common-sdl2.cpp b/examples/ggwave-common-sdl2.cpp index 63e7d09..aeb4a47 100644 --- a/examples/ggwave-common-sdl2.cpp +++ b/examples/ggwave-common-sdl2.cpp @@ -34,8 +34,8 @@ GGWave *g_ggWave = nullptr; // JS interface extern "C" { EMSCRIPTEN_KEEPALIVE - int setText(int textLength, const char * text, int protocolId) { - g_ggWave->init(textLength, text, g_ggWave->getTxProtocols()[protocolId]); + int sendData(int textLength, const char * text, int protocolId, int volume) { + g_ggWave->init(textLength, text, g_ggWave->getTxProtocols()[protocolId], volume); return 0; } diff --git a/examples/ggwave-gui/main.cpp b/examples/ggwave-gui/main.cpp index fff1df8..e9176ce 100644 --- a/examples/ggwave-gui/main.cpp +++ b/examples/ggwave-gui/main.cpp @@ -137,7 +137,7 @@ int main(int argc, char** argv) { } if (inputCurrent.update) { - ggWave->init(inputCurrent.message.data.size(), inputCurrent.message.data.data(), ggWave->getTxProtocols()[2]); + ggWave->init(inputCurrent.message.data.size(), inputCurrent.message.data.data(), ggWave->getTxProtocols()[2], 50); inputCurrent.update = false; } diff --git a/include/ggwave/ggwave.h b/include/ggwave/ggwave.h index c856f25..3161708 100644 --- a/include/ggwave/ggwave.h +++ b/include/ggwave/ggwave.h @@ -26,17 +26,26 @@ public: int freqStart; int framesPerTx; int bytesPerTx; - int volume; int nDataBitsPerTx() const { return 8*bytesPerTx; } }; + using TxProtocols = std::vector; + + const TxProtocols kTxProtocols { + { "Normal", 40, 9, 3, }, + { "Fast", 40, 6, 3, }, + { "Fastest", 40, 3, 3, }, + { "[U] Normal", 320, 9, 3, }, + { "[U] Fast", 320, 6, 3, }, + { "[U] Fastest", 320, 3, 3, }, + }; + using AmplitudeData = std::array; using AmplitudeData16 = std::array; using SpectrumData = std::array; using RecordedData = std::array; using TxRxData = std::array; - using TxProtocols = std::vector; using CBQueueAudio = std::function; using CBDequeueAudio = std::function; @@ -49,7 +58,7 @@ public: int sampleSizeBytesOut); ~GGWave(); - bool init(int textLength, const char * stext, const TxProtocol & aProtocol); + bool init(int textLength, const char * stext, const TxProtocol & aProtocol, const int volume); void send(const CBQueueAudio & cbQueueAudio); void receive(const CBDequeueAudio & CBDequeueAudio); @@ -63,24 +72,15 @@ public: const int & getSampleSizeBytesIn() const { return m_sampleSizeBytesIn; } const int & getSampleSizeBytesOut() const { return m_sampleSizeBytesOut; } - const float & getSampleRateIn() const { return m_sampleRateIn; } + const float & getSampleRateIn() const { return m_sampleRateIn; } + + const TxProtocol & getDefultTxProtocol() const { return kTxProtocols[1]; } + const TxProtocols & getTxProtocols() const { return kTxProtocols; } const TxRxData & getRxData() const { return m_rxData; } - const TxProtocol & getDefultTxProtocol() const { return txProtocols[1]; } - const TxProtocols & getTxProtocols() const { return txProtocols; } - int takeRxData(TxRxData & dst); private: - const TxProtocols txProtocols { - { "Normal", 40, 9, 3, 50 }, - { "Fast", 40, 6, 3, 50 }, - { "Fastest", 40, 3, 3, 50 }, - { "[U] Normal", 320, 9, 3, 50 }, - { "[U] Fast", 320, 6, 3, 50 }, - { "[U] Fastest", 320, 3, 3, 50 }, - }; - int maxFramesPerTx() const; int minBytesPerTx() const; @@ -139,6 +139,7 @@ private: bool m_hasNewTxData; int m_nECCBytesPerTx; int m_sendDataLength; + float m_sendVolume; int m_txDataLength; TxRxData m_txData; diff --git a/src/ggwave.cpp b/src/ggwave.cpp index ec36319..ef8ec3c 100644 --- a/src/ggwave.cpp +++ b/src/ggwave.cpp @@ -130,13 +130,13 @@ GGWave::GGWave( m_encodedDataOffset(3), m_rsLength(new RS::ReedSolomon(1, m_encodedDataOffset - 1)) { - init(0, "", getDefultTxProtocol()); + init(0, "", getDefultTxProtocol(), 0); } GGWave::~GGWave() { } -bool GGWave::init(int textLength, const char * stext, const TxProtocol & aProtocol) { +bool GGWave::init(int textLength, const char * stext, const TxProtocol & aProtocol, const int volume) { if (textLength > kMaxLength) { printf("Truncating data from %d to 140 bytes\n", textLength); textLength = kMaxLength; @@ -144,6 +144,7 @@ bool GGWave::init(int textLength, const char * stext, const TxProtocol & aProtoc m_txProtocol = aProtocol; m_txDataLength = textLength; + m_sendVolume = ((double)(volume))/100.0f; const uint8_t * text = reinterpret_cast(stext); @@ -191,8 +192,6 @@ void GGWave::send(const CBQueueAudio & cbQueueAudio) { int frameId = 0; - float sendVolume = ((double)(m_txProtocol.volume))/100.0f; - AmplitudeData outputBlock; AmplitudeData16 outputBlock16; @@ -264,9 +263,9 @@ void GGWave::send(const CBQueueAudio & cbQueueAudio) { for (int i = 0; i < m_nBitsInMarker; ++i) { if (i%2 == 0) { - ::addAmplitudeSmooth(bit1Amplitude[i], outputBlock, sendVolume, 0, samplesPerFrameOut, frameId, m_nMarkerFrames); + ::addAmplitudeSmooth(bit1Amplitude[i], outputBlock, m_sendVolume, 0, samplesPerFrameOut, frameId, m_nMarkerFrames); } else { - ::addAmplitudeSmooth(bit0Amplitude[i], outputBlock, sendVolume, 0, samplesPerFrameOut, frameId, m_nMarkerFrames); + ::addAmplitudeSmooth(bit0Amplitude[i], outputBlock, m_sendVolume, 0, samplesPerFrameOut, frameId, m_nMarkerFrames); } } } else if (frameId < m_nMarkerFrames + m_nPostMarkerFrames) { @@ -274,9 +273,9 @@ void GGWave::send(const CBQueueAudio & cbQueueAudio) { for (int i = 0; i < m_nBitsInMarker; ++i) { if (i%2 == 0) { - ::addAmplitudeSmooth(bit0Amplitude[i], outputBlock, sendVolume, 0, samplesPerFrameOut, frameId - m_nMarkerFrames, m_nPostMarkerFrames); + ::addAmplitudeSmooth(bit0Amplitude[i], outputBlock, m_sendVolume, 0, samplesPerFrameOut, frameId - m_nMarkerFrames, m_nPostMarkerFrames); } else { - ::addAmplitudeSmooth(bit1Amplitude[i], outputBlock, sendVolume, 0, samplesPerFrameOut, frameId - m_nMarkerFrames, m_nPostMarkerFrames); + ::addAmplitudeSmooth(bit1Amplitude[i], outputBlock, m_sendVolume, 0, samplesPerFrameOut, frameId - m_nMarkerFrames, m_nPostMarkerFrames); } } } else if (frameId < @@ -305,9 +304,9 @@ void GGWave::send(const CBQueueAudio & cbQueueAudio) { ++nFreq; if (k%2) { - ::addAmplitudeSmooth(bit0Amplitude[k/2], outputBlock, sendVolume, 0, samplesPerFrameOut, cycleModMain, m_txProtocol.framesPerTx); + ::addAmplitudeSmooth(bit0Amplitude[k/2], outputBlock, m_sendVolume, 0, samplesPerFrameOut, cycleModMain, m_txProtocol.framesPerTx); } else { - ::addAmplitudeSmooth(bit1Amplitude[k/2], outputBlock, sendVolume, 0, samplesPerFrameOut, cycleModMain, m_txProtocol.framesPerTx); + ::addAmplitudeSmooth(bit1Amplitude[k/2], outputBlock, m_sendVolume, 0, samplesPerFrameOut, cycleModMain, m_txProtocol.framesPerTx); } } } else if (frameId < @@ -319,9 +318,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, sendVolume, 0, samplesPerFrameOut, fId, m_nMarkerFrames); + addAmplitudeSmooth(bit0Amplitude[i], outputBlock, m_sendVolume, 0, samplesPerFrameOut, fId, m_nMarkerFrames); } else { - addAmplitudeSmooth(bit1Amplitude[i], outputBlock, sendVolume, 0, samplesPerFrameOut, fId, m_nMarkerFrames); + addAmplitudeSmooth(bit1Amplitude[i], outputBlock, m_sendVolume, 0, samplesPerFrameOut, fId, m_nMarkerFrames); } } } else { @@ -408,7 +407,7 @@ void GGWave::receive(const CBDequeueAudio & CBDequeueAudio) { std::unique_ptr rsData; bool isValid = false; - for (const auto & rxProtocol : txProtocols) { + for (const auto & rxProtocol : kTxProtocols) { // skip Rx protocol if start frequency is different from detected one if (rxProtocol.freqStart != m_markerFreqStart) { continue; @@ -529,7 +528,7 @@ void GGWave::receive(const CBDequeueAudio & CBDequeueAudio) { if (m_receivingData == false) { bool isReceiving = false; - for (const auto & rxProtocol : txProtocols) { + for (const auto & rxProtocol : kTxProtocols) { bool isReceivingCur = true; for (int i = 0; i < m_nBitsInMarker; ++i) { @@ -568,7 +567,7 @@ void GGWave::receive(const CBDequeueAudio & CBDequeueAudio) { } else { bool isEnded = false; - for (const auto & rxProtocol : txProtocols) { + for (const auto & rxProtocol : kTxProtocols) { bool isEndedCur = true; for (int i = 0; i < m_nBitsInMarker; ++i) { @@ -613,15 +612,15 @@ int GGWave::takeRxData(TxRxData & dst) { int GGWave::maxFramesPerTx() const { int res = 0; - for (const auto & protocol : txProtocols) { + for (const auto & protocol : kTxProtocols) { res = std::max(res, protocol.framesPerTx); } return res; } int GGWave::minBytesPerTx() const { - int res = txProtocols.front().framesPerTx; - for (const auto & protocol : txProtocols) { + int res = kTxProtocols.front().framesPerTx; + for (const auto & protocol : kTxProtocols) { res = std::min(res, protocol.bytesPerTx); } return res; From cf35ed33c9c236e21323fac9023616aacbe5a43c Mon Sep 17 00:00:00 2001 From: Georgi Gerganov Date: Sat, 5 Dec 2020 14:00:32 +0200 Subject: [PATCH 09/12] wip : fftw without std::complex --- include/ggwave/ggwave.h | 5 ++- src/ggwave.cpp | 73 ++++++++++++++++++++++++++--------------- 2 files changed, 48 insertions(+), 30 deletions(-) diff --git a/include/ggwave/ggwave.h b/include/ggwave/ggwave.h index 3161708..a5443a5 100644 --- a/include/ggwave/ggwave.h +++ b/include/ggwave/ggwave.h @@ -1,7 +1,6 @@ #pragma once #include -#include #include #include #include @@ -121,8 +120,8 @@ private: int m_framesToAnalyze; int m_framesToRecord; - std::array m_fftIn; - std::array, kMaxSamplesPerFrame> m_fftOut; + std::array m_fftIn; // real + std::array m_fftOut; // complex AmplitudeData m_sampleAmplitude; SpectrumData m_sampleSpectrum; diff --git a/src/ggwave.cpp b/src/ggwave.cpp index ef8ec3c..feae05c 100644 --- a/src/ggwave.cpp +++ b/src/ggwave.cpp @@ -28,31 +28,48 @@ int reverse(int N, int n) { return p; } -void ordina(std::complex* f1, int N) { - std::complex f2[GGWave::kMaxSamplesPerFrame]; - for(int i = 0; i < N; i++) - f2[i] = f1[reverse(N, i)]; - for(int j = 0; j < N; j++) - f1[j] = f2[j]; +void ordina(float * f1, int N) { + 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]; + f2[2*i + 1] = f1[2*ir + 1]; + } + for (int j = 0; j < N; j++) { + f1[2*j + 0] = f2[2*j + 0]; + f1[2*j + 1] = f2[2*j + 1]; + } } -void transform(std::complex* f, int N) { +void transform(float * f, int N) { ordina(f, N); //first: reverse order - std::complex *W; - W = (std::complex *)malloc(N / 2 * sizeof(std::complex)); - W[1] = std::polar(1., -2. * M_PI / N); - W[0] = 1; - for(int i = 2; i < N / 2; i++) - W[i] = pow(W[1], i); + float * W; + W = (float *)malloc(N*sizeof(float)); + W[2*1 + 0] = cos(-2.*M_PI/N); + W[2*1 + 1] = sin(-2.*M_PI/N); + W[2*0 + 0] = 1; + W[2*0 + 1] = 0; + for (int i = 2; i < N / 2; i++) { + W[2*i + 0] = cos(-2.*i*M_PI/N); + W[2*i + 1] = sin(-2.*i*M_PI/N); + } int n = 1; int a = N / 2; for(int j = 0; j < log2(N); j++) { for(int i = 0; i < N; i++) { if(!(i & n)) { - std::complex temp = f[i]; - std::complex Temp = W[(i * a) % (n * a)] * f[i + n]; - f[i] = temp + Temp; - f[i + n] = temp - Temp; + int wi = (i * a) % (n * a); + int fi = i + n; + float a = W[2*wi + 0]; + float b = W[2*wi + 1]; + float c = f[2*fi + 0]; + float d = f[2*fi + 1]; + float temp[2] = { f[2*i + 0], f[2*i + 1] }; + float Temp[2] = { a*c - b*d, b*c + a*d }; + f[2*i + 0] = temp[0] + Temp[0]; + f[2*i + 1] = temp[1] + Temp[1]; + f[2*fi + 0] = temp[0] - Temp[0]; + f[2*fi + 1] = temp[1] - Temp[1]; } } n *= 2; @@ -61,16 +78,18 @@ void transform(std::complex* f, int N) { free(W); } -void FFT(std::complex* f, int N, float d) { +void FFT(float * f, int N, float d) { transform(f, N); - for(int i = 0; i < N; i++) - f[i] *= d; //multiplying by step + for (int i = 0; i < N; i++) { + f[2*i + 0] *= d; + f[2*i + 1] *= d; + } } -void FFT(float * src, std::complex* dst, int N, float d) { +void FFT(float * src, float * dst, int N, float d) { for (int i = 0; i < N; ++i) { - dst[i].real(src[i]); - dst[i].imag(0); + dst[2*i + 0] = src[i]; + dst[2*i + 1] = 0.0f; } FFT(dst, N, d); } @@ -177,8 +196,8 @@ bool GGWave::init(int textLength, const char * stext, const TxProtocol & aProtoc m_rxData.fill(0); for (int i = 0; i < m_samplesPerFrame; ++i) { - m_fftOut[i].real(0.0f); - m_fftOut[i].imag(0.0f); + m_fftOut[2*i + 0] = 0.0f; + m_fftOut[2*i + 1] = 0.0f; } return true; @@ -378,7 +397,7 @@ void GGWave::receive(const CBDequeueAudio & CBDequeueAudio) { double fsum = 0.0; for (int i = 0; i < m_samplesPerFrame; ++i) { - m_sampleSpectrum[i] = (m_fftOut[i].real()*m_fftOut[i].real() + m_fftOut[i].imag()*m_fftOut[i].imag()); + m_sampleSpectrum[i] = (m_fftOut[2*i + 0]*m_fftOut[2*i + 0] + m_fftOut[2*i + 1]*m_fftOut[2*i + 1]); fsum += m_sampleSpectrum[i]; } for (int i = 1; i < m_samplesPerFrame/2; ++i) { @@ -440,7 +459,7 @@ void GGWave::receive(const CBDequeueAudio & CBDequeueAudio) { FFT(m_fftIn.data(), m_fftOut.data(), m_samplesPerFrame, 1.0); for (int i = 0; i < m_samplesPerFrame; ++i) { - m_sampleSpectrum[i] = (m_fftOut[i].real()*m_fftOut[i].real() + m_fftOut[i].imag()*m_fftOut[i].imag()); + m_sampleSpectrum[i] = (m_fftOut[2*i + 0]*m_fftOut[2*i + 0] + m_fftOut[2*i + 1]*m_fftOut[2*i + 1]); } for (int i = 1; i < m_samplesPerFrame/2; ++i) { m_sampleSpectrum[i] += m_sampleSpectrum[m_samplesPerFrame - i]; From 1ee4274c48dd2048662409088bdc1b878ce3a8a6 Mon Sep 17 00:00:00 2001 From: Georgi Gerganov Date: Sat, 5 Dec 2020 14:06:45 +0200 Subject: [PATCH 10/12] wip : info about Rx protocol --- include/ggwave/ggwave.h | 7 ++++--- src/ggwave.cpp | 12 +++++++++--- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/include/ggwave/ggwave.h b/include/ggwave/ggwave.h index a5443a5..3f5282e 100644 --- a/include/ggwave/ggwave.h +++ b/include/ggwave/ggwave.h @@ -77,6 +77,7 @@ public: const TxProtocols & getTxProtocols() const { return kTxProtocols; } const TxRxData & getRxData() const { return m_rxData; } + const TxProtocol & getRxProtocol() const { return m_rxProtocol; } int takeRxData(TxRxData & dst); private: @@ -106,14 +107,11 @@ private: const int m_encodedDataOffset; // Rx - bool m_hasNewRxData; bool m_receivingData; bool m_analyzingData; int m_markerFreqStart; - int m_recvDuration_frames; - int m_lastRxDataLength; int m_framesLeftToAnalyze; int m_framesLeftToRecord; @@ -126,7 +124,10 @@ private: AmplitudeData m_sampleAmplitude; SpectrumData m_sampleSpectrum; + bool m_hasNewRxData; + int m_lastRxDataLength; TxRxData m_rxData; + TxProtocol m_rxProtocol; int m_historyId = 0; AmplitudeData m_sampleAmplitudeAverage; diff --git a/src/ggwave.cpp b/src/ggwave.cpp index feae05c..5aa8d6d 100644 --- a/src/ggwave.cpp +++ b/src/ggwave.cpp @@ -423,6 +423,7 @@ void GGWave::receive(const CBDequeueAudio & CBDequeueAudio) { const int stepsPerFrame = 16; const int step = m_samplesPerFrame/stepsPerFrame; + int lastRSLength = -1; std::unique_ptr rsData; bool isValid = false; @@ -468,7 +469,7 @@ void GGWave::receive(const CBDequeueAudio & CBDequeueAudio) { uint8_t curByte = 0; for (int i = 0; i < 2*rxProtocol.bytesPerTx; ++i) { double freq = m_hzPerSample*rxProtocol.freqStart; - int bin = std::round(freq*m_ihzPerSample) + i*16; + int bin = std::round(freq*m_ihzPerSample) + 16*i; int kmax = 0; double amax = 0.0; @@ -498,9 +499,13 @@ void GGWave::receive(const CBDequeueAudio & CBDequeueAudio) { } if (knownLength) { - rsData.reset(new RS::ReedSolomon(m_rxData[0], ::getECCBytesForLength(m_rxData[0]))); - int decodedLength = m_rxData[0]; + + if (decodedLength != lastRSLength) { + rsData.reset(new RS::ReedSolomon(decodedLength, ::getECCBytesForLength(decodedLength))); + lastRSLength = decodedLength; + } + if (rsData->Decode(m_txDataEncoded.data() + m_encodedDataOffset, m_rxData.data()) == 0) { if (m_rxData[0] != 0) { std::string s((char *) m_rxData.data(), decodedLength); @@ -511,6 +516,7 @@ void GGWave::receive(const CBDequeueAudio & CBDequeueAudio) { isValid = true; m_hasNewRxData = true; m_lastRxDataLength = decodedLength; + m_rxProtocol = rxProtocol; } } } From 368962c82da91c04d835e09b5e1db22799c27baf Mon Sep 17 00:00:00 2001 From: Georgi Gerganov Date: Sat, 5 Dec 2020 14:45:18 +0200 Subject: [PATCH 11/12] ggwave-gui : add volume and protocol selection --- examples/ggwave-gui/main.cpp | 244 +++++++++++++++++++++++------------ include/ggwave/ggwave.h | 3 + src/ggwave.cpp | 5 +- 3 files changed, 170 insertions(+), 82 deletions(-) diff --git a/examples/ggwave-gui/main.cpp b/examples/ggwave-gui/main.cpp index e9176ce..2df3cf4 100644 --- a/examples/ggwave-gui/main.cpp +++ b/examples/ggwave-gui/main.cpp @@ -96,7 +96,8 @@ int main(int argc, char** argv) { bool received; std::time_t timestamp; std::string data; - std::string protocol; + int protocolId; + float volume; }; struct State { @@ -137,7 +138,12 @@ int main(int argc, char** argv) { } if (inputCurrent.update) { - ggWave->init(inputCurrent.message.data.size(), inputCurrent.message.data.data(), ggWave->getTxProtocols()[2], 50); + ggWave->init( + inputCurrent.message.data.size(), + inputCurrent.message.data.data(), + ggWave->getTxProtocols()[inputCurrent.message.protocolId], + 100*inputCurrent.message.volume); + inputCurrent.update = false; } @@ -146,7 +152,13 @@ int main(int argc, char** argv) { lastRxDataLength = ggWave->takeRxData(lastRxData); if (lastRxDataLength > 0) { buffer.stateCore.update = true; - buffer.stateCore.message = { true, std::time(nullptr), std::string((char *) lastRxData.data(), lastRxDataLength), "" }; + buffer.stateCore.message = { + true, + std::time(nullptr), + std::string((char *) lastRxData.data(), lastRxDataLength), + ggWave->getRxProtocolId(), + 0, + }; } { @@ -176,6 +188,20 @@ int main(int argc, char** argv) { } } + enum class WindowId { + Settings, + Messages, + Commands, + }; + + struct Settings { + int protocolId = 1; + float volume = 0.25f; + }; + + static WindowId windowId = WindowId::Messages; + static Settings settings; + static char inputBuf[256]; static bool doInputFocus = false; @@ -222,106 +248,162 @@ int main(int argc, char** argv) { ImGui::InvisibleButton("StatusBar", { ImGui::GetContentRegionAvailWidth(), statusBarHeight }); if (ImGui::Button(ICON_FA_COGS, { menuButtonHeight, menuButtonHeight } )) { + windowId = WindowId::Settings; } ImGui::SameLine(); if (ImGui::Button(ICON_FA_COMMENT_ALT " Messages", { 0.5f*ImGui::GetContentRegionAvailWidth(), menuButtonHeight })) { + windowId = WindowId::Messages; } ImGui::SameLine(); if (ImGui::Button(ICON_FA_LIST_UL " Commands", { 1.0f*ImGui::GetContentRegionAvailWidth(), menuButtonHeight })) { + windowId = WindowId::Commands; } - const float messagesInputHeight = ImGui::GetTextLineHeightWithSpacing(); - const float messagesHistoryHeigthMax = ImGui::GetContentRegionAvail().y - messagesInputHeight - 2.0f*style.ItemSpacing.x; - float messagesHistoryHeigth = messagesHistoryHeigthMax; + if (windowId == WindowId::Settings) { + ImGui::BeginChild("Settings:main", ImGui::GetContentRegionAvail(), true); + ImGui::Text("Waver v0.1"); + ImGui::Separator(); - // no automatic screen resize support for iOS + ImGui::Text("%s", ""); + ImGui::Text("Sample rate (capture): %g, %d B/sample", ggWave->getSampleRateIn(), ggWave->getSampleSizeBytesIn()); + ImGui::Text("Sample rate (playback): %g, %d B/sample", ggWave->getSampleRateOut(), ggWave->getSampleSizeBytesOut()); + + static float kLabelWidth = 100.0f; + + // volume + ImGui::Text("%s", ""); + { + auto posSave = ImGui::GetCursorScreenPos(); + ImGui::Text("Volume: "); + ImGui::SetCursorScreenPos({ posSave.x + kLabelWidth, posSave.y }); + } + ImGui::SliderFloat("##volume", &settings.volume, 0.0f, 1.0f); + + // protocol + ImGui::Text("%s", ""); + { + auto posSave = ImGui::GetCursorScreenPos(); + ImGui::Text("Tx Protocol: "); + ImGui::SetCursorScreenPos({ posSave.x + kLabelWidth, posSave.y }); + } + ImGui::SameLine(); + if (ImGui::BeginCombo("##protocol", ggWave->getTxProtocols()[settings.protocolId].name)) { + for (int i = 0; i < (int) ggWave->getTxProtocols().size(); ++i) { + const bool isSelected = (settings.protocolId == i); + if (ImGui::Selectable(ggWave->getTxProtocols()[i].name, isSelected)) { + settings.protocolId = i; + } + + if (isSelected) { + ImGui::SetItemDefaultFocus(); + } + } + ImGui::EndCombo(); + } + + ImGui::EndChild(); + } + + if (windowId == WindowId::Messages) { + const float messagesInputHeight = ImGui::GetTextLineHeightWithSpacing(); + const float messagesHistoryHeigthMax = ImGui::GetContentRegionAvail().y - messagesInputHeight - 2.0f*style.ItemSpacing.x; + float messagesHistoryHeigth = messagesHistoryHeigthMax; + + // no automatic screen resize support for iOS #ifdef IOS - if (displaySize.x < displaySize.y) { - if (isTextInput) { - messagesHistoryHeigth -= 0.5f*messagesHistoryHeigthMax*std::min(tShowKeyboard, ImGui::GetTime() - tStartInput) / tShowKeyboard; + if (displaySize.x < displaySize.y) { + if (isTextInput) { + messagesHistoryHeigth -= 0.5f*messagesHistoryHeigthMax*std::min(tShowKeyboard, ImGui::GetTime() - tStartInput) / tShowKeyboard; + } else { + messagesHistoryHeigth -= 0.5f*messagesHistoryHeigthMax - 0.5f*messagesHistoryHeigthMax*std::min(tShowKeyboard, ImGui::GetTime() - tEndInput) / tShowKeyboard; + } } else { - messagesHistoryHeigth -= 0.5f*messagesHistoryHeigthMax - 0.5f*messagesHistoryHeigthMax*std::min(tShowKeyboard, ImGui::GetTime() - tEndInput) / tShowKeyboard; + if (isTextInput) { + messagesHistoryHeigth -= 0.5f*displaySize.y*std::min(tShowKeyboard, ImGui::GetTime() - tStartInput) / tShowKeyboard; + } else { + messagesHistoryHeigth -= 0.5f*displaySize.y - 0.5f*displaySize.y*std::min(tShowKeyboard, ImGui::GetTime() - tEndInput) / tShowKeyboard; + } } - } else { - if (isTextInput) { - messagesHistoryHeigth -= 0.5f*displaySize.y*std::min(tShowKeyboard, ImGui::GetTime() - tStartInput) / tShowKeyboard; - } else { - messagesHistoryHeigth -= 0.5f*displaySize.y - 0.5f*displaySize.y*std::min(tShowKeyboard, ImGui::GetTime() - tEndInput) / tShowKeyboard; - } - } #endif - ImGui::BeginChild("Messages:history", { ImGui::GetContentRegionAvailWidth(), messagesHistoryHeigth }, true); + ImGui::BeginChild("Messages:history", { ImGui::GetContentRegionAvailWidth(), messagesHistoryHeigth }, true); - for (int i = 0; i < (int) messageHistory.size(); ++i) { - ImGui::PushID(i); - const auto & message = messageHistory[i]; - if (message.received) { - ImGui::TextColored({ 0.0f, 1.0f, 0.0f, 1.0f }, "[%s] Recv:", ::toTimeString(message.timestamp)); - ImGui::SameLine(); - if (ImGui::SmallButton("Resend")) { - buffer.inputUI.update = true; - buffer.inputUI.message = { false, std::time(nullptr), message.data, "" }; + for (int i = 0; i < (int) messageHistory.size(); ++i) { + ImGui::PushID(i); + const auto & message = messageHistory[i]; + if (message.received) { + ImGui::TextColored({ 0.0f, 1.0f, 0.0f, 1.0f }, "[%s] Recv (%s):", ::toTimeString(message.timestamp), ggWave->getTxProtocols()[message.protocolId].name); + ImGui::SameLine(); + if (ImGui::SmallButton("Resend")) { + buffer.inputUI.update = true; + buffer.inputUI.message = { false, std::time(nullptr), message.data, message.protocolId, settings.volume }; - messageHistory.push_back(buffer.inputUI.message); + messageHistory.push_back(buffer.inputUI.message); + } + ImGui::Text("%s", message.data.c_str()); + } else { + ImGui::TextColored({ 1.0f, 1.0f, 0.0f, 1.0f }, "[%s] Sent (%s):", ::toTimeString(message.timestamp), ggWave->getTxProtocols()[message.protocolId].name); + ImGui::SameLine(); + if (ImGui::SmallButton("Resend")) { + buffer.inputUI.update = true; + buffer.inputUI.message = { false, std::time(nullptr), message.data, message.protocolId, settings.volume }; + + messageHistory.push_back(buffer.inputUI.message); + } + ImGui::Text("%s", message.data.c_str()); } - ImGui::Text("%s", message.data.c_str()); - } else { - ImGui::TextColored({ 1.0f, 1.0f, 0.0f, 1.0f }, "[%s] Sent:", ::toTimeString(message.timestamp)); - ImGui::SameLine(); - if (ImGui::SmallButton("Resend")) { - buffer.inputUI.update = true; - buffer.inputUI.message = { false, std::time(nullptr), message.data, "" }; - - messageHistory.push_back(buffer.inputUI.message); - } - ImGui::Text("%s", message.data.c_str()); + ImGui::Text("%s", ""); + ImGui::PopID(); + } + + if (scrollMessagesToBottom) { + ImGui::SetScrollHereY(); + scrollMessagesToBottom = false; + } + + ImVec2 mouse_delta = ImGui::GetIO().MouseDelta; + ScrollWhenDraggingOnVoid(ImVec2(0.0f, -mouse_delta.y), ImGuiMouseButton_Left); + ImGui::EndChild(); + + if (doInputFocus) { + ImGui::SetKeyboardFocusHere(); + doInputFocus = false; + } + ImGui::PushItemWidth(ImGui::GetContentRegionAvailWidth() - ImGui::CalcTextSize(sendButtonText).x - 2*style.ItemSpacing.x); + ImGui::InputText("##Messages:Input", inputBuf, 256, ImGuiInputTextFlags_EnterReturnsTrue); + ImGui::PopItemWidth(); + if (ImGui::IsItemActive() && isTextInput == false) { + SDL_StartTextInput(); + isTextInput = true; + tStartInput = ImGui::GetTime(); + } + bool requestStopTextInput = false; + if (ImGui::IsItemDeactivated()) { + requestStopTextInput = true; + } + ImGui::SameLine(); + if (ImGui::Button(sendButtonText) && inputBuf[0] != 0) { + buffer.inputUI.update = true; + buffer.inputUI.message = { false, std::time(nullptr), std::string(inputBuf), settings.protocolId, settings.volume }; + + messageHistory.push_back(buffer.inputUI.message); + + inputBuf[0] = 0; + doInputFocus = true; + } + if (!ImGui::IsItemHovered() && requestStopTextInput) { + SDL_StopTextInput(); + isTextInput = false; + tEndInput = ImGui::GetTime(); } - ImGui::Text("%s", ""); - ImGui::PopID(); } - if (scrollMessagesToBottom) { - ImGui::SetScrollHereY(); - scrollMessagesToBottom = false; - } - - ImVec2 mouse_delta = ImGui::GetIO().MouseDelta; - ScrollWhenDraggingOnVoid(ImVec2(0.0f, -mouse_delta.y), ImGuiMouseButton_Left); - ImGui::EndChild(); - - if (doInputFocus) { - ImGui::SetKeyboardFocusHere(); - doInputFocus = false; - } - ImGui::PushItemWidth(ImGui::GetContentRegionAvailWidth() - ImGui::CalcTextSize(sendButtonText).x - 2*style.ItemSpacing.x); - ImGui::InputText("##Messages:Input", inputBuf, 256, ImGuiInputTextFlags_EnterReturnsTrue); - ImGui::PopItemWidth(); - if (ImGui::IsItemActive() && isTextInput == false) { - SDL_StartTextInput(); - isTextInput = true; - tStartInput = ImGui::GetTime(); - } - bool requestStopTextInput = false; - if (ImGui::IsItemDeactivated()) { - requestStopTextInput = true; - } - ImGui::SameLine(); - if (ImGui::Button(sendButtonText) && inputBuf[0] != 0) { - buffer.inputUI.update = true; - buffer.inputUI.message = { false, std::time(nullptr), std::string(inputBuf), "" }; - - messageHistory.push_back(buffer.inputUI.message); - - inputBuf[0] = 0; - doInputFocus = true; - } - if (!ImGui::IsItemHovered() && requestStopTextInput) { - SDL_StopTextInput(); - isTextInput = false; - tEndInput = ImGui::GetTime(); + if (windowId == WindowId::Commands) { + ImGui::BeginChild("Commands:main", ImGui::GetContentRegionAvail(), true); + ImGui::Text("Todo"); + ImGui::EndChild(); } ImGui::End(); diff --git a/include/ggwave/ggwave.h b/include/ggwave/ggwave.h index 3f5282e..c781b55 100644 --- a/include/ggwave/ggwave.h +++ b/include/ggwave/ggwave.h @@ -72,12 +72,14 @@ public: const int & getSampleSizeBytesOut() const { return m_sampleSizeBytesOut; } const float & getSampleRateIn() const { return m_sampleRateIn; } + const float & getSampleRateOut() const { return m_sampleRateOut; } const TxProtocol & getDefultTxProtocol() const { return kTxProtocols[1]; } const TxProtocols & getTxProtocols() const { return kTxProtocols; } const TxRxData & getRxData() const { return m_rxData; } const TxProtocol & getRxProtocol() const { return m_rxProtocol; } + const int & getRxProtocolId() const { return m_rxProtocolId; } int takeRxData(TxRxData & dst); private: @@ -128,6 +130,7 @@ private: int m_lastRxDataLength; TxRxData m_rxData; TxProtocol m_rxProtocol; + int m_rxProtocolId; int m_historyId = 0; AmplitudeData m_sampleAmplitudeAverage; diff --git a/src/ggwave.cpp b/src/ggwave.cpp index 5aa8d6d..dd7a7f8 100644 --- a/src/ggwave.cpp +++ b/src/ggwave.cpp @@ -427,7 +427,9 @@ void GGWave::receive(const CBDequeueAudio & CBDequeueAudio) { std::unique_ptr rsData; bool isValid = false; - for (const auto & rxProtocol : kTxProtocols) { + for (int rxProtocolId = 0; rxProtocolId < (int) kTxProtocols.size(); ++rxProtocolId) { + const auto & rxProtocol = kTxProtocols[rxProtocolId]; + // skip Rx protocol if start frequency is different from detected one if (rxProtocol.freqStart != m_markerFreqStart) { continue; @@ -517,6 +519,7 @@ void GGWave::receive(const CBDequeueAudio & CBDequeueAudio) { m_hasNewRxData = true; m_lastRxDataLength = decodedLength; m_rxProtocol = rxProtocol; + m_rxProtocolId = rxProtocolId; } } } From 44d0ea293be6fd20f79646ea0679f33388408e42 Mon Sep 17 00:00:00 2001 From: Georgi Gerganov Date: Sat, 5 Dec 2020 14:49:56 +0200 Subject: [PATCH 12/12] minor --- examples/ggwave-cli/main.cpp | 16 +++++++++++----- examples/ggwave-gui/main.cpp | 6 ------ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/examples/ggwave-cli/main.cpp b/examples/ggwave-cli/main.cpp index 8ad03dc..88cff63 100644 --- a/examples/ggwave-cli/main.cpp +++ b/examples/ggwave-cli/main.cpp @@ -14,11 +14,7 @@ int main(int argc, char** argv) { printf("Usage: %s [-cN] [-pN] [-tN]\n", argv[0]); printf(" -cN - select capture device N\n"); printf(" -pN - select playback device N\n"); - printf(" -tN - transmission protocol:\n"); - printf(" -t0 : Normal\n"); - printf(" -t1 : Fast (default)\n"); - printf(" -t2 : Fastest\n"); - printf(" -t3 : Ultrasonic\n"); + printf(" -tN - transmission protocol\n"); printf("\n"); auto argm = parseCmdArguments(argc, argv); @@ -33,6 +29,16 @@ int main(int argc, char** argv) { auto ggWave = GGWave_instance(); + printf("Available Tx protocols:\n"); + for (int i = 0; i < (int) ggWave->getTxProtocols().size(); ++i) { + printf(" -t%d : %s\n", i, ggWave->getTxProtocols()[i].name); + } + + if (txProtocol < 0 || txProtocol > (int) ggWave->getTxProtocols().size()) { + fprintf(stderr, "Unknown Tx protocol %d\n", txProtocol); + return -3; + } + printf("Selecting Tx protocol %d\n", txProtocol); std::mutex mutex; diff --git a/examples/ggwave-gui/main.cpp b/examples/ggwave-gui/main.cpp index 2df3cf4..3c48d49 100644 --- a/examples/ggwave-gui/main.cpp +++ b/examples/ggwave-gui/main.cpp @@ -50,17 +50,11 @@ int main(int argc, char** argv) { printf("Usage: %s [-cN] [-pN] [-tN]\n", argv[0]); printf(" -cN - select capture device N\n"); printf(" -pN - select playback device N\n"); - printf(" -tN - transmission protocol:\n"); - printf(" -t0 : Normal\n"); - printf(" -t1 : Fast (default)\n"); - printf(" -t2 : Fastest\n"); - printf(" -t3 : Ultrasonic\n"); printf("\n"); auto argm = parseCmdArguments(argc, argv); int captureId = argm["c"].empty() ? 0 : std::stoi(argm["c"]); int playbackId = argm["p"].empty() ? 0 : std::stoi(argm["p"]); - int txProtocol = argm["t"].empty() ? 1 : std::stoi(argm["t"]); if (GGWave_init(playbackId, captureId) == false) { fprintf(stderr, "Failed to initialize GGWave\n");