diff --git a/examples/ggwave-cli/main.cpp b/examples/ggwave-cli/main.cpp index fff813d..9be6e68 100644 --- a/examples/ggwave-cli/main.cpp +++ b/examples/ggwave-cli/main.cpp @@ -18,6 +18,7 @@ int main(int argc, char** argv) { printf(" -pN - select playback device N\n"); printf(" -tN - transmission protocol\n"); printf(" -lN - fixed payload length of size N, N in [1, %d]\n", GGWave::kMaxLengthFixed); + printf(" -v - print generated tones on resend\n"); printf("\n"); auto argm = parseCmdArguments(argc, argv); @@ -25,6 +26,7 @@ int main(int argc, char** argv) { int playbackId = argm["p"].empty() ? 0 : std::stoi(argm["p"]); int txProtocol = argm["t"].empty() ? 1 : std::stoi(argm["t"]); int payloadLength = argm["l"].empty() ? -1 : std::stoi(argm["l"]); + bool printTones = argm.find("v") == argm.end() ? false : true; if (GGWave_init(playbackId, captureId, payloadLength) == false) { fprintf(stderr, "Failed to initialize GGWave\n"); @@ -51,13 +53,26 @@ int main(int argc, char** argv) { std::string inputOld = ""; while (true) { std::string input; - std::cout << "Enter text: "; + printf("Enter text: "); + fflush(stdout); getline(std::cin, input); if (input.empty()) { - std::cout << "Re-sending ... " << std::endl; + printf("Re-sending ...\n"); input = inputOld; + + if (printTones) { + printf("Printing generated waveform tones (Hz):\n"); + auto waveformTones = ggWave->getWaveformTones(); + for (int i = 0; i < (int) waveformTones.size(); ++i) { + printf(" - frame %3d: ", i); + for (int j = 0; j < (int) waveformTones[i].size(); ++j) { + printf("%8.2f ", waveformTones[i][j].freq_hz); + } + printf("\n"); + } + } } else { - std::cout << "Sending ... " << std::endl; + printf("Sending ...\n"); } { std::lock_guard lock(mutex); diff --git a/include/ggwave/ggwave.h b/include/ggwave/ggwave.h index 668df15..58f7d37 100644 --- a/include/ggwave/ggwave.h +++ b/include/ggwave/ggwave.h @@ -275,6 +275,14 @@ class GGWave { return kTxProtocols; } + struct ToneData { + double freq_hz; + double duration_ms; + }; + + using Tones = std::vector; + using WaveformTones = std::vector; + using AmplitudeData = std::vector; using AmplitudeDataI16 = std::vector; using SpectrumData = std::vector; @@ -355,6 +363,12 @@ class GGWave { static const TxProtocol & getTxProtocol(int id) { return getTxProtocols().at(TxProtocolId(id)); } static const TxProtocol & getTxProtocol(TxProtocolId id) { return getTxProtocols().at(id); } + // get a list of the tones generated for the last waveform + // + // Call this method after calling encode() to get a list of the tones participating in the generated waveform + // + const WaveformTones & getWaveformTones() { return m_waveformTones; } + bool takeTxAmplitudeI16(AmplitudeDataI16 & dst); // Rx @@ -473,6 +487,7 @@ class GGWave { TxRxData m_outputBlockTmp; AmplitudeDataI16 m_outputBlockI16; AmplitudeDataI16 m_txAmplitudeDataI16; + WaveformTones m_waveformTones; // Impl // todo : move all members inside Impl diff --git a/src/ggwave.cpp b/src/ggwave.cpp index 3a54ee6..2f7b914 100644 --- a/src/ggwave.cpp +++ b/src/ggwave.cpp @@ -546,18 +546,26 @@ bool GGWave::encode(const CBWaveformOut & cbWaveformOut) { float factor = kBaseSampleRate/m_sampleRateOut; uint32_t offset = 0; + m_waveformTones.clear(); + while (m_hasNewTxData) { std::fill(m_outputBlock.begin(), m_outputBlock.end(), 0.0f); std::uint16_t nFreq = 0; + m_waveformTones.push_back({}); + if (frameId < m_nMarkerFrames) { nFreq = m_nBitsInMarker; for (int i = 0; i < m_nBitsInMarker; ++i) { + m_waveformTones.back().push_back({}); + m_waveformTones.back().back().duration_ms = (1000.0*m_samplesPerFrame)/kBaseSampleRate; if (i%2 == 0) { ::addAmplitudeSmooth(bit1Amplitude[i], m_outputBlock, m_sendVolume, 0, m_samplesPerFrame, frameId, m_nMarkerFrames); + m_waveformTones.back().back().freq_hz = bitFreq(m_txProtocol, i); } else { ::addAmplitudeSmooth(bit0Amplitude[i], m_outputBlock, m_sendVolume, 0, m_samplesPerFrame, frameId, m_nMarkerFrames); + m_waveformTones.back().back().freq_hz = bitFreq(m_txProtocol, i) + m_hzPerSample; } } } else if (frameId < m_nMarkerFrames + totalDataFrames) { @@ -583,10 +591,14 @@ bool GGWave::encode(const CBWaveformOut & cbWaveformOut) { if (dataBits[k] == 0) continue; ++nFreq; + m_waveformTones.back().push_back({}); + m_waveformTones.back().back().duration_ms = (1000.0*m_samplesPerFrame)/kBaseSampleRate; if (k%2) { ::addAmplitudeSmooth(bit0Amplitude[k/2], m_outputBlock, m_sendVolume, 0, m_samplesPerFrame, cycleModMain, m_txProtocol.framesPerTx); + m_waveformTones.back().back().freq_hz = bitFreq(m_txProtocol, k/2) + m_hzPerSample; } else { ::addAmplitudeSmooth(bit1Amplitude[k/2], m_outputBlock, m_sendVolume, 0, m_samplesPerFrame, cycleModMain, m_txProtocol.framesPerTx); + m_waveformTones.back().back().freq_hz = bitFreq(m_txProtocol, k/2); } } } else if (frameId < m_nMarkerFrames + totalDataFrames + m_nMarkerFrames) { @@ -594,10 +606,14 @@ bool GGWave::encode(const CBWaveformOut & cbWaveformOut) { int fId = frameId - (m_nMarkerFrames + totalDataFrames); for (int i = 0; i < m_nBitsInMarker; ++i) { + m_waveformTones.back().push_back({}); + m_waveformTones.back().back().duration_ms = (1000.0*m_samplesPerFrame)/kBaseSampleRate; if (i%2 == 0) { addAmplitudeSmooth(bit0Amplitude[i], m_outputBlock, m_sendVolume, 0, m_samplesPerFrame, fId, m_nMarkerFrames); + m_waveformTones.back().back().freq_hz = bitFreq(m_txProtocol, i) + m_hzPerSample; } else { addAmplitudeSmooth(bit1Amplitude[i], m_outputBlock, m_sendVolume, 0, m_samplesPerFrame, fId, m_nMarkerFrames); + m_waveformTones.back().back().freq_hz = bitFreq(m_txProtocol, i); } } } else {