From 4794508e04cd8b7e3668e9212c33afb11047e5c1 Mon Sep 17 00:00:00 2001 From: Georgi Gerganov Date: Tue, 7 Jun 2022 21:22:54 +0300 Subject: [PATCH] remove-stl : prepare migration to ggvector --- examples/ggwave-cli/main.cpp | 10 +- examples/r2t2/main.cpp | 26 +-- include/ggwave/ggwave.h | 71 +++++- src/ggwave.cpp | 436 ++++++++++++++++++----------------- 4 files changed, 304 insertions(+), 239 deletions(-) diff --git a/examples/ggwave-cli/main.cpp b/examples/ggwave-cli/main.cpp index f459015..c44a442 100644 --- a/examples/ggwave-cli/main.cpp +++ b/examples/ggwave-cli/main.cpp @@ -68,13 +68,15 @@ int main(int argc, char** argv) { if (printTones) { printf("Printing generated waveform tones (Hz):\n"); + const auto & protocol = protocols[txProtocolId]; const auto tones = ggWave->txTones(); for (int i = 0; i < (int) tones.size(); ++i) { - printf(" - frame %3d: ", i); - for (int j = 0; j < (int) tones[i].size(); ++j) { - printf("%8.2f ", tones[i][j].freq_hz); + if (tones[i] < 0) { + printf(" - end tx\n"); + continue; } - printf("\n"); + const auto freq_hz = (protocol.freqStart + tones[i])*ggWave->hzPerSample(); + printf(" - tone %3d: %f\n", i, freq_hz); } } } else { diff --git a/examples/r2t2/main.cpp b/examples/r2t2/main.cpp index ea8dee5..1540bd9 100644 --- a/examples/r2t2/main.cpp +++ b/examples/r2t2/main.cpp @@ -132,26 +132,12 @@ int main(int argc, char** argv) { ggWave.init(message.size(), message.data(), GGWave::TxProtocolId(txProtocolId), 10); ggWave.encode(); - int nFrames = 0; - double lastF = -1.0f; - - auto tones = ggWave.txTones(); - for (auto & tonesCur : tones) { - if (tonesCur.size() == 0) continue; - const auto & tone = tonesCur.front(); - if (tone.freq_hz != lastF) { - if (nFrames > 0) { - processTone(fd, lastF, nFrames*tone.duration_ms, useBeep, printTones, printArduino); - } - nFrames = 0; - lastF = tone.freq_hz; - } - ++nFrames; - } - - if (nFrames > 0) { - const auto & tone = tones.front().front(); - processTone(fd, lastF, nFrames*tone.duration_ms, useBeep, printTones, printArduino); + const auto & protocol = protocols[txProtocolId]; + const auto tones = ggWave.txTones(); + const auto duration_ms = protocol.txDuration_ms(ggWave.samplesPerFrame(), ggWave.sampleRateOut()); + for (auto & tone : tones) { + const auto freq_hz = (protocol.freqStart + tone)*ggWave.hzPerSample(); + processTone(fd, freq_hz, duration_ms, useBeep, printTones, printArduino); } return 0; diff --git a/include/ggwave/ggwave.h b/include/ggwave/ggwave.h index 8ae3574..b52284a 100644 --- a/include/ggwave/ggwave.h +++ b/include/ggwave/ggwave.h @@ -27,6 +27,8 @@ extern "C" { // C interface // +#define GGWAVE_MAX_INSTANCES 4 + // Data format of the audio samples typedef enum { GGWAVE_SAMPLE_FORMAT_UNDEFINED, @@ -328,6 +330,8 @@ extern "C" { // C++ interface // +#include + template struct ggvector { private: @@ -337,6 +341,7 @@ private: public: using value_type = T; + ggvector() : m_data(nullptr), m_size(0) {} ggvector(T * data, int size) : m_data(data), m_size(size) {} T & operator[](int i) { @@ -347,6 +352,16 @@ public: return m_data[i]; } + void copy(const ggvector & other) { + if (this != &other) { + if (m_size != other.m_size) { + // should never happen + assert(false); + } + memcpy(m_data, other.m_data, m_size * sizeof(T)); + } + } + int size() const { return m_size; } @@ -364,6 +379,31 @@ public: } }; +template +struct ggmatrix { +private: + T * m_data; + int m_size0; + int m_size1; + +public: + using value_type = T; + + ggmatrix(T * data, int size0, int size1) : m_data(data), m_size0(size0), m_size1(size1) {} + + ggvector operator[](int i) { + return ggvector(m_data + i * m_size1, m_size1); + } + + T & operator()(int i, int j) { + return m_data[i * m_size1 + j]; + } + + int size0() const { + return m_size0; + } +}; + #include #include #include @@ -402,7 +442,11 @@ public: bool enabled; + int nTones() const { return (2*bytesPerTx)/extra; } int nDataBitsPerTx() const { return 8*bytesPerTx; } + int txDuration_ms(int samplesPerFrame, float sampleRate) const { + return extra*framesPerTx*((1000.0f*samplesPerFrame)/sampleRate); + } }; using TxProtocol = Protocol; @@ -490,14 +534,19 @@ public: static RxProtocols & rx(); }; - // TODO: need more efficient way to store this - struct ToneData { - float freq_hz; - float duration_ms; - }; + using Tone = int8_t; - using TonesPerFrame = std::vector; - using Tones = std::vector; + // generated tones + // + // Each Tone is the bin index of the tone frequency. + // For protocol p: + // - freq_hz = (p.freqStart + Tone) * hzPerSample + // - duration_ms = p.txDuration_ms(samplesPerFrame, sampleRate) + // + // If the protocol is mono-tone, each element of the vector corresponds to a single tone. + // Otherwise, the tones within a single Tx are separated by value of -1 + // + using Tones = ggvector; using Amplitude = std::vector; using AmplitudeI16 = std::vector; @@ -541,6 +590,9 @@ public: GGWave(const Parameters & parameters); ~GGWave(); + bool prepare(const Parameters & parameters); + bool alloc(void * p, int & n); + // set file stream for the internal ggwave logging // // By default, ggwave prints internal log messages to stderr. @@ -607,6 +659,7 @@ public: int sampleSizeInp() const; int sampleSizeOut() const; + float hzPerSample() const; float sampleRateInp() const; float sampleRateOut() const; SampleFormat sampleFormatInp() const; @@ -623,7 +676,7 @@ public: // Call this method after calling encode() to get a list of the tones // participating in the generated waveform // - const Tones & txTones() const; + const Tones txTones() const; // true if there is data pending to be transmitted bool txHasData() const; @@ -742,6 +795,7 @@ private: int maxFramesPerTx(const Protocols & protocols, bool excludeMT) const; int minBytesPerTx(const Protocols & protocols) const; int maxBytesPerTx(const Protocols & protocols) const; + int maxTonesPerTx(const Protocols & protocols) const; double bitFreq(const Protocol & p, int bit) const; @@ -855,6 +909,7 @@ private: TxRxData outputTmp; AmplitudeI16 outputI16; + int nTones = 0; Tones tones; } m_tx; diff --git a/src/ggwave.cpp b/src/ggwave.cpp index f15de6a..7c12bc8 100644 --- a/src/ggwave.cpp +++ b/src/ggwave.cpp @@ -32,7 +32,7 @@ namespace { FILE * g_fptr = stderr; -std::vector g_instances; +GGWave * g_instances[GGWAVE_MAX_INSTANCES]; double linear_interp(double first_number, double second_number, double fraction) { return (first_number + ((second_number - first_number)*fraction)); @@ -52,52 +52,58 @@ ggwave_Parameters ggwave_getDefaultParameters(void) { extern "C" ggwave_Instance ggwave_init(const ggwave_Parameters parameters) { - static ggwave_Instance curId = 0; + for (ggwave_Instance id = 0; id < GGWAVE_MAX_INSTANCES; ++id) { + if (g_instances[id] == nullptr) { + g_instances[id] = new GGWave({ + parameters.payloadLength, + parameters.sampleRateInp, + parameters.sampleRateOut, + parameters.sampleRate, + parameters.samplesPerFrame, + parameters.soundMarkerThreshold, + parameters.sampleFormatInp, + parameters.sampleFormatOut, + parameters.operatingMode}); - if ((int) g_instances.size() < curId + 1) { - g_instances.resize(curId + 1, nullptr); + return id; + } } - g_instances[curId] = new GGWave({ - parameters.payloadLength, - parameters.sampleRateInp, - parameters.sampleRateOut, - parameters.sampleRate, - parameters.samplesPerFrame, - parameters.soundMarkerThreshold, - parameters.sampleFormatInp, - parameters.sampleFormatOut, - parameters.operatingMode}); + ggprintf("Failed to create GGWave instance - reached maximum number of instances (%d)\n", GGWAVE_MAX_INSTANCES); - return curId++; + return -1; } extern "C" -void ggwave_free(ggwave_Instance instance) { - if ((int) g_instances.size() > instance && g_instances[instance]) { - delete (GGWave *) g_instances[instance]; - g_instances[instance] = nullptr; +void ggwave_free(ggwave_Instance id) { + if (id >= 0 && id < GGWAVE_MAX_INSTANCES && g_instances[id]) { + delete (GGWave *) g_instances[id]; + g_instances[id] = nullptr; + + return; } + + ggprintf("Failed to free GGWave instance - invalid GGWave instance id %d\n", id); } extern "C" int ggwave_encode( - ggwave_Instance instance, + ggwave_Instance id, const void * payloadBuffer, int payloadSize, ggwave_ProtocolId protocolId, int volume, void * waveformBuffer, int query) { - GGWave * ggWave = (GGWave *) g_instances[instance]; + GGWave * ggWave = (GGWave *) g_instances[id]; if (ggWave == nullptr) { - ggprintf("Invalid GGWave instance %d\n", instance); + ggprintf("Invalid GGWave instance %d\n", id); return -1; } if (ggWave->init(payloadSize, (const char *) payloadBuffer, protocolId, volume) == false) { - ggprintf("Failed to initialize Tx transmission for GGWave instance %d\n", instance); + ggprintf("Failed to initialize Tx transmission for GGWave instance %d\n", id); return -1; } @@ -111,7 +117,7 @@ int ggwave_encode( const int nBytes = ggWave->encode(); if (nBytes == 0) { - ggprintf("Failed to encode data - GGWave instance %d\n", instance); + ggprintf("Failed to encode data - GGWave instance %d\n", id); return -1; } @@ -126,14 +132,14 @@ int ggwave_encode( extern "C" int ggwave_decode( - ggwave_Instance instance, + ggwave_Instance id, const void * waveformBuffer, int waveformSize, void * payloadBuffer) { - GGWave * ggWave = (GGWave *) g_instances[instance]; + GGWave * ggWave = (GGWave *) g_instances[id]; if (ggWave->decode(waveformBuffer, waveformSize) == false) { - ggprintf("Failed to decode data - GGWave instance %d\n", instance); + ggprintf("Failed to decode data - GGWave instance %d\n", id); return -1; } @@ -152,15 +158,15 @@ int ggwave_decode( extern "C" int ggwave_ndecode( - ggwave_Instance instance, + ggwave_Instance id, const void * waveformBuffer, int waveformSize, void * payloadBuffer, int payloadSize) { - GGWave * ggWave = (GGWave *) g_instances[instance]; + GGWave * ggWave = (GGWave *) g_instances[id]; if (ggWave->decode(waveformBuffer, waveformSize) == false) { - ggprintf("Failed to decode data - GGWave instance %d\n", instance); + ggprintf("Failed to decode data - GGWave instance %d\n", id); return -1; } @@ -326,6 +332,53 @@ GGWave::RxProtocols & GGWave::Protocols::rx() { return protocols; } +template +void ggalloc(std::vector & v, int n, void * buf, int & bufSize) { + if (buf == nullptr) { + bufSize += n*sizeof(T); + return; + } + + v.resize(n); + bufSize += n*sizeof(T); +} + +template +void ggalloc(std::vector> & v, int n, int m, void * buf, int & bufSize) { + if (buf == nullptr) { + bufSize += n*m*sizeof(T); + return; + } + + v.resize(n); + for (int i = 0; i < n; i++) { + v[i].resize(m); + } + bufSize += n*m*sizeof(T); +} + +template +void ggalloc(ggvector & v, int n, void * buf, int & bufSize) { + if (buf == nullptr) { + bufSize += n*sizeof(T); + return; + } + + v = ggvector((T *)((char *) buf + bufSize), n); + bufSize += n*sizeof(T); +} + +template +void ggalloc(ggmatrix & v, int n, int m, void * buf, int & bufSize) { + if (buf == nullptr) { + bufSize += n*m*sizeof(T); + return; + } + + v = ggmatrix((T *)((char *) buf + bufSize), n, m); + bufSize += n*m*sizeof(T); +} + // // GGWave // @@ -355,10 +408,6 @@ GGWave::GGWave(const Parameters & parameters) : m_needResampling (m_sampleRateInp != m_sampleRate || m_sampleRateOut != m_sampleRate), m_txOnlyTones (parameters.operatingMode & GGWAVE_OPERATING_MODE_TX_ONLY_TONES), m_isDSSEnabled (parameters.operatingMode & GGWAVE_OPERATING_MODE_USE_DSS), - - // common - m_dataEncoded (kMaxDataSize), - m_resampler(nullptr) { if (m_sampleSizeInp == 0) { @@ -386,151 +435,7 @@ GGWave::GGWave(const Parameters & parameters) : return; } - m_heapSize = 0; - - // compute required memory: - { - if (m_isRxEnabled) { - m_heapSize += (2*m_samplesPerFrame)*sizeof(decltype(m_rx.fftOut)::value_type); - m_heapSize += (3 + sqrt(m_samplesPerFrame/2))*sizeof(decltype(m_rx.fftWorkI)::value_type); - m_heapSize += (m_samplesPerFrame/2)*sizeof(decltype(m_rx.fftWorkF)::value_type); - - m_heapSize += (m_samplesPerFrame)*sizeof(decltype(m_rx.spectrum)::value_type); - m_heapSize += (m_needResampling ? m_samplesPerFrame + 128 : m_samplesPerFrame)*sizeof(decltype(m_rx.amplitude)::value_type); - m_heapSize += (m_needResampling ? 8*m_samplesPerFrame : m_samplesPerFrame)*sizeof(decltype(m_rx.amplitudeResampled)::value_type); - m_heapSize += (m_needResampling ? 8*m_samplesPerFrame*m_sampleSizeInp : m_samplesPerFrame*m_sampleSizeInp)*sizeof(decltype(m_rx.amplitudeTmp)::value_type); - - m_heapSize += (kMaxDataSize)*sizeof(decltype(m_rx.data)::value_type); - - if (m_isFixedPayloadLength) { - const int totalLength = m_payloadLength + getECCBytesForLength(m_payloadLength); - const int totalTxs = (totalLength + minBytesPerTx(m_rx.protocols) - 1)/minBytesPerTx(m_rx.protocols); - - m_heapSize += (totalTxs*maxFramesPerTx(m_rx.protocols, false))*sizeof(decltype(m_rx.spectrumHistoryFixed)::value_type); - m_heapSize += (2*totalLength)*sizeof(decltype(m_rx.detectedBins)::value_type); - m_heapSize += (2*16*maxBytesPerTx(m_rx.protocols))*sizeof(decltype(m_rx.detectedTones)::value_type); - } else { - m_heapSize += (kMaxRecordedFrames*m_samplesPerFrame)*sizeof(decltype(m_rx.amplitudeRecorded)::value_type); - m_heapSize += (m_samplesPerFrame)*sizeof(decltype(m_rx.amplitudeAverage)::value_type); - - // TODO: this is incorrect: - m_heapSize += (kMaxSpectrumHistory)*sizeof(decltype(m_rx.amplitudeHistory)::value_type); - } - } - - if (m_isTxEnabled) { - const int maxDataBits = 2*16*maxBytesPerTx(m_tx.protocols); - - m_heapSize += (kMaxDataSize)*sizeof(decltype(m_tx.data)::value_type); - m_heapSize += (maxDataBits)*sizeof(decltype(m_tx.dataBits)::value_type); - - if (m_txOnlyTones == false) { - m_heapSize += (maxDataBits)*sizeof(decltype(m_tx.phaseOffsets)::value_type); - // TODO: ... - } - - m_heapSize += (140*sizeof(decltype(m_tx.tones)::value_type)); - } - - const auto maxLength = m_isFixedPayloadLength ? m_payloadLength : kMaxLengthVariable; - - if (m_isFixedPayloadLength == false) { - m_heapSize += RS::ReedSolomon::getWorkSize_bytes(1, m_encodedDataOffset - 1); - } - m_heapSize += RS::ReedSolomon::getWorkSize_bytes(maxLength, getECCBytesForLength(maxLength)); - } - - if (m_isRxEnabled) { - m_rx.samplesNeeded = m_samplesPerFrame; - - m_rx.fftOut.resize(2*m_samplesPerFrame); - m_rx.fftWorkI.resize(3 + sqrt(m_samplesPerFrame/2)); - m_rx.fftWorkF.resize(m_samplesPerFrame/2); - m_rx.fftWorkI[0] = 0; - - m_rx.spectrum.resize(m_samplesPerFrame); - m_rx.amplitude.resize(m_needResampling ? m_samplesPerFrame + 128 : m_samplesPerFrame); // small extra space because sometimes resampling needs a few more samples - m_rx.amplitudeResampled.resize(m_needResampling ? 8*m_samplesPerFrame : m_samplesPerFrame); // min input sampling rate is 0.125*m_sampleRate - m_rx.amplitudeTmp.resize(m_needResampling ? 8*m_samplesPerFrame*m_sampleSizeInp : m_samplesPerFrame*m_sampleSizeInp); - - m_rx.data.resize(kMaxDataSize); - - m_rx.protocol = {}; - m_rx.protocolId = GGWAVE_PROTOCOL_COUNT; - m_rx.protocols = Protocols::rx(); - - if (m_isFixedPayloadLength) { - if (m_payloadLength > kMaxLengthFixed) { - ggprintf("Invalid payload legnth: %d, max: %d\n", m_payloadLength, kMaxLengthFixed); - return; - } - - const int totalLength = m_payloadLength + getECCBytesForLength(m_payloadLength); - const int totalTxs = (totalLength + minBytesPerTx(m_rx.protocols) - 1)/minBytesPerTx(m_rx.protocols); - - m_rx.spectrumHistoryFixed.resize(totalTxs*maxFramesPerTx(m_rx.protocols, false)); - m_rx.detectedBins.resize(2*totalLength); - m_rx.detectedTones.resize(2*16*maxBytesPerTx(m_rx.protocols)); - } else { - // variable payload length - m_rx.amplitudeRecorded.resize(kMaxRecordedFrames*m_samplesPerFrame); - m_rx.amplitudeAverage.resize(m_samplesPerFrame); - m_rx.amplitudeHistory.resize(kMaxSpectrumHistory); - } - - for (auto & s : m_rx.amplitudeHistory) { - s.resize(m_samplesPerFrame); - } - - for (auto & s : m_rx.spectrumHistoryFixed) { - s.resize(m_samplesPerFrame); - } - } - - if (m_isTxEnabled) { - m_tx.protocols = Protocols::tx(); - - const int maxDataBits = 2*16*maxBytesPerTx(m_tx.protocols); - - m_tx.data.resize(kMaxDataSize); - m_tx.dataBits.resize(maxDataBits); - - if (m_txOnlyTones == false) { - m_tx.phaseOffsets.resize(maxDataBits); - m_tx.bit0Amplitude.resize(maxDataBits); - for (auto & a : m_tx.bit0Amplitude) { - a.resize(m_samplesPerFrame); - } - m_tx.bit1Amplitude.resize(maxDataBits); - for (auto & a : m_tx.bit1Amplitude) { - a.resize(m_samplesPerFrame); - } - - m_tx.output.resize(m_samplesPerFrame); - m_tx.outputResampled.resize(2*m_samplesPerFrame); - m_tx.outputTmp.resize(kMaxRecordedFrames*m_samplesPerFrame*m_sampleSizeOut); - m_tx.outputI16.resize(kMaxRecordedFrames*m_samplesPerFrame); - } - - // TODO - m_tx.tones.reserve(140); - } - - // pre-allocate Reed-Solomon memory buffers - { - const auto maxLength = m_isFixedPayloadLength ? m_payloadLength : kMaxLengthVariable; - - if (m_isFixedPayloadLength == false) { - m_workRSLength.resize(RS::ReedSolomon::getWorkSize_bytes(1, m_encodedDataOffset - 1)); - } - m_workRSData.resize(RS::ReedSolomon::getWorkSize_bytes(maxLength, getECCBytesForLength(maxLength))); - } - - if (m_needResampling) { - m_resampler = new Resampler(); - } - - init("", {}, 0); + prepare(parameters); } GGWave::~GGWave() { @@ -540,6 +445,123 @@ GGWave::~GGWave() { } } +bool GGWave::prepare(const Parameters & parameters) { + // TODO: initialize members from parameters + + m_heap = nullptr; + m_heapSize = 0; + + if (this->alloc(m_heap, m_heapSize) == false) { + ggprintf("Error: failed to compute the size of the required memory\n"); + return false; + } + + m_heap = malloc(m_heapSize); + + if (this->alloc(m_heap, m_heapSize) == false) { + ggprintf("Error: failed to allocate the required memory: %d\n", m_heapSize); + return false; + } + + if (m_isRxEnabled) { + m_rx.samplesNeeded = m_samplesPerFrame; + + m_rx.fftWorkI[0] = 0; + + m_rx.protocol = {}; + m_rx.protocolId = GGWAVE_PROTOCOL_COUNT; + m_rx.protocols = Protocols::rx(); + } + + if (m_isTxEnabled) { + m_tx.protocols = Protocols::tx(); + } + + // TODO: avoid new + if (m_needResampling) { + m_resampler = new Resampler(); + } + + return init("", {}, 0); +} + +bool GGWave::alloc(void * p, int & n) { + n = 0; + + // common + ::ggalloc(m_dataEncoded, kMaxDataSize, p, n); + + if (m_isRxEnabled) { + ::ggalloc(m_rx.fftOut, 2*m_samplesPerFrame, p, n); + ::ggalloc(m_rx.fftWorkI, 3 + sqrt(m_samplesPerFrame/2), p, n); + ::ggalloc(m_rx.fftWorkF, m_samplesPerFrame/2, p, n); + + ::ggalloc(m_rx.spectrum, m_samplesPerFrame, p, n); + // small extra space because sometimes resampling needs a few more samples: + ::ggalloc(m_rx.amplitude, m_needResampling ? m_samplesPerFrame + 128 : m_samplesPerFrame, p, n); + // min input sampling rate is 0.125*m_sampleRate: + ::ggalloc(m_rx.amplitudeResampled, m_needResampling ? 8*m_samplesPerFrame : m_samplesPerFrame, p, n); + ::ggalloc(m_rx.amplitudeTmp, m_needResampling ? 8*m_samplesPerFrame*m_sampleSizeInp : m_samplesPerFrame*m_sampleSizeInp, p, n); + + ::ggalloc(m_rx.data, kMaxDataSize, p, n); + + if (m_isFixedPayloadLength) { + if (m_payloadLength > kMaxLengthFixed) { + ggprintf("Invalid payload length: %d, max: %d\n", m_payloadLength, kMaxLengthFixed); + return false; + } + + const int totalLength = m_payloadLength + getECCBytesForLength(m_payloadLength); + const int totalTxs = (totalLength + minBytesPerTx(Protocols::rx()) - 1)/minBytesPerTx(Protocols::rx()); + + ::ggalloc(m_rx.spectrumHistoryFixed, totalTxs*maxFramesPerTx(Protocols::rx(), false), m_samplesPerFrame, p, n); + ::ggalloc(m_rx.detectedBins, 2*totalLength, p, n); + ::ggalloc(m_rx.detectedTones, 2*16*maxBytesPerTx(Protocols::rx()), p, n); + } else { + // variable payload length + ::ggalloc(m_rx.amplitudeRecorded, kMaxRecordedFrames*m_samplesPerFrame, p, n); + ::ggalloc(m_rx.amplitudeAverage, m_samplesPerFrame, p, n); + ::ggalloc(m_rx.amplitudeHistory, kMaxSpectrumHistory, m_samplesPerFrame, p, n); + } + } + + if (m_isTxEnabled) { + const int maxDataBits = 2*16*maxBytesPerTx(Protocols::tx()); + + ::ggalloc(m_tx.data, kMaxDataSize, p, n); + ::ggalloc(m_tx.dataBits, maxDataBits, p, n); + + if (m_txOnlyTones == false) { + ::ggalloc(m_tx.phaseOffsets, maxDataBits, p, n); + ::ggalloc(m_tx.bit0Amplitude, maxDataBits, m_samplesPerFrame, p, n); + ::ggalloc(m_tx.bit1Amplitude, maxDataBits, m_samplesPerFrame, p, n); + ::ggalloc(m_tx.output, m_samplesPerFrame, p, n); + ::ggalloc(m_tx.outputResampled, 2*m_samplesPerFrame, p, n); + ::ggalloc(m_tx.outputTmp, kMaxRecordedFrames*m_samplesPerFrame*m_sampleSizeOut, p, n); + ::ggalloc(m_tx.outputI16, kMaxRecordedFrames*m_samplesPerFrame, p, n); + } + + const int maxLength = m_isFixedPayloadLength ? m_payloadLength : kMaxLengthVariable; + const int totalLength = maxLength + getECCBytesForLength(maxLength); + const int totalTxs = (totalLength + minBytesPerTx(Protocols::rx()) - 1)/minBytesPerTx(Protocols::tx()); + const int maxTones = m_isFixedPayloadLength ? maxTonesPerTx(Protocols::tx()) : m_nBitsInMarker; + + ::ggalloc(m_tx.tones, maxTones*totalTxs + (maxTones > 1 ? totalTxs : 0), p, n); + } + + // pre-allocate Reed-Solomon memory buffers + { + const auto maxLength = m_isFixedPayloadLength ? m_payloadLength : kMaxLengthVariable; + + if (m_isFixedPayloadLength == false) { + ::ggalloc(m_workRSLength, RS::ReedSolomon::getWorkSize_bytes(1, m_encodedDataOffset - 1), p, n); + } + ::ggalloc(m_workRSData, RS::ReedSolomon::getWorkSize_bytes(maxLength, getECCBytesForLength(maxLength)), p, n); + } + + return true; +} + void GGWave::setLogFile(FILE * fptr) { g_fptr = fptr; } @@ -636,7 +658,7 @@ bool GGWave::init(int dataSize, const char * dataBuffer, TxProtocolId protocolId m_rx.framesToRecord = 0; m_rx.framesLeftToRecord = 0; - std::fill(m_rx.spectrum.begin(), m_rx.spectrum.end(), 0); + std::fill(m_rx.spectrum.begin(), m_rx.spectrum.end(), 0); std::fill(m_rx.amplitude.begin(), m_rx.amplitude.end(), 0); for (auto & s : m_rx.amplitudeHistory) { std::fill(s.begin(), s.end(), 0); @@ -707,19 +729,11 @@ uint32_t GGWave::encode() { int frameId = 0; bool hasData = m_tx.hasData; - m_tx.tones.clear(); + m_tx.nTones = 0; while (hasData) { - m_tx.tones.push_back({}); - if (frameId < m_nMarkerFrames) { for (int i = 0; i < m_nBitsInMarker; ++i) { - m_tx.tones.back().push_back({}); - m_tx.tones.back().back().duration_ms = (1000.0*m_samplesPerFrame)/m_sampleRate; - if (i%2 == 0) { - m_tx.tones.back().back().freq_hz = bitFreq(m_tx.protocol, i); - } else { - m_tx.tones.back().back().freq_hz = bitFreq(m_tx.protocol, i) + m_hzPerSample; - } + m_tx.tones[m_tx.nTones++] = 2*i + i%2; } } else if (frameId < m_nMarkerFrames + totalDataFrames) { int dataOffset = frameId - m_nMarkerFrames; @@ -752,30 +766,22 @@ uint32_t GGWave::encode() { for (int k = 0; k < 2*m_tx.protocol.bytesPerTx*16; ++k) { if (m_tx.dataBits[k] == 0) continue; - m_tx.tones.back().push_back({}); - m_tx.tones.back().back().duration_ms = (1000.0*m_samplesPerFrame)/m_sampleRate; - if (k%2) { - m_tx.tones.back().back().freq_hz = bitFreq(m_tx.protocol, k/2) + m_hzPerSample; - } else { - m_tx.tones.back().back().freq_hz = bitFreq(m_tx.protocol, k/2); - } + m_tx.tones[m_tx.nTones++] = k; } } else if (frameId < m_nMarkerFrames + totalDataFrames + m_nMarkerFrames) { for (int i = 0; i < m_nBitsInMarker; ++i) { - m_tx.tones.back().push_back({}); - m_tx.tones.back().back().duration_ms = (1000.0*m_samplesPerFrame)/m_sampleRate; - if (i%2 == 0) { - m_tx.tones.back().back().freq_hz = bitFreq(m_tx.protocol, i) + m_hzPerSample; - } else { - m_tx.tones.back().back().freq_hz = bitFreq(m_tx.protocol, i); - } + m_tx.tones[m_tx.nTones++] = 2*i + (1 - i%2); } } else { hasData = false; break; } - ++frameId; + if (m_tx.protocol.nTones() > 1) { + m_tx.tones[m_tx.nTones++] = -1; + } + + frameId += m_tx.protocol.framesPerTx; } if (m_txOnlyTones) { @@ -1131,6 +1137,7 @@ int GGWave::samplesPerFrame() const { return m_samplesPerFrame; } int GGWave::sampleSizeInp() const { return m_sampleSizeInp; } int GGWave::sampleSizeOut() const { return m_sampleSizeOut; } +float GGWave::hzPerSample() const { return m_hzPerSample; } float GGWave::sampleRateInp() const { return m_sampleRateInp; } float GGWave::sampleRateOut() const { return m_sampleRateOut; } GGWave::SampleFormat GGWave::sampleFormatInp() const { return m_sampleFormatInp; } @@ -1142,7 +1149,7 @@ int GGWave::heapSize() const { return m_heapSize; } // Tx // -const GGWave::Tones & GGWave::txTones() const { return m_tx.tones; } +const GGWave::Tones GGWave::txTones() const { return { m_tx.tones.data(), m_tx.nTones }; } bool GGWave::txHasData() const { return m_tx.hasData; } @@ -1150,8 +1157,11 @@ bool GGWave::txTakeAmplitudeI16(AmplitudeI16 & dst) { if (m_tx.lastAmplitudeSize == 0) return false; if ((int) dst.size() < m_tx.lastAmplitudeSize) { - dst.resize(m_tx.lastAmplitudeSize); + ggprintf("GGWave::txTakeAmplitudeI16: dst buffer too small (%d < %d)\n", (int) dst.size(), m_tx.lastAmplitudeSize); + + return false; } + std::copy(m_tx.outputI16.begin(), m_tx.outputI16.begin() + m_tx.lastAmplitudeSize, dst.begin()); m_tx.lastAmplitudeSize = 0; @@ -1909,6 +1919,18 @@ int GGWave::maxBytesPerTx(const Protocols & protocols) const { return res; } +int GGWave::maxTonesPerTx(const Protocols & protocols) const { + int res = 1; + for (int i = 0; i < protocols.size(); ++i) { + const auto & protocol = protocols[i]; + if (protocol.enabled == false) { + continue; + } + res = std::max(res, protocol.nTones()); + } + return res; +} + double GGWave::bitFreq(const Protocol & p, int bit) const { return m_hzPerSample*p.freqStart + m_freqDelta_hz*bit; }