diff --git a/examples/arduino-rx/arduino-rx.ino b/examples/arduino-rx/arduino-rx.ino index 3965fdc..9927155 100644 --- a/examples/arduino-rx/arduino-rx.ino +++ b/examples/arduino-rx/arduino-rx.ino @@ -52,11 +52,12 @@ void loop() { p.sampleRate = frequency; p.sampleFormatInp = GGWAVE_SAMPLE_FORMAT_I16; p.samplesPerFrame = 128; - p.payloadLength = 16; + p.payloadLength = 4; p.operatingMode = GGWAVE_OPERATING_MODE_RX; GGWave ggwave(p); ggwave.setRxProtocols({ + //{ GGWAVE_TX_PROTOCOL_MT_FASTEST, ggwave.getTxProtocol(GGWAVE_TX_PROTOCOL_MT_FASTEST) }, { GGWAVE_TX_PROTOCOL_DT_FASTEST, ggwave.getTxProtocol(GGWAVE_TX_PROTOCOL_DT_FASTEST) }, }); Serial.println("Instance initialized"); diff --git a/examples/ggwave-cli/main.cpp b/examples/ggwave-cli/main.cpp index 1b097a2..ddd9d2a 100644 --- a/examples/ggwave-cli/main.cpp +++ b/examples/ggwave-cli/main.cpp @@ -41,7 +41,7 @@ int main(int argc, char** argv) { printf(" -t%d : %s\n", protocol.first, protocol.second.name); } - if (txProtocolId < 0 || txProtocolId > (int) ggWave->getTxProtocols().size()) { + if (txProtocolId < 0) { fprintf(stderr, "Unknown Tx protocol %d\n", txProtocolId); return -3; } diff --git a/include/ggwave/ggwave.h b/include/ggwave/ggwave.h index 5673b10..429f3c9 100644 --- a/include/ggwave/ggwave.h +++ b/include/ggwave/ggwave.h @@ -44,6 +44,9 @@ extern "C" { GGWAVE_TX_PROTOCOL_DT_NORMAL, GGWAVE_TX_PROTOCOL_DT_FAST, GGWAVE_TX_PROTOCOL_DT_FASTEST, + GGWAVE_TX_PROTOCOL_MT_NORMAL, + GGWAVE_TX_PROTOCOL_MT_FAST, + GGWAVE_TX_PROTOCOL_MT_FASTEST, GGWAVE_TX_PROTOCOL_CUSTOM_0, GGWAVE_TX_PROTOCOL_CUSTOM_1, @@ -331,6 +334,7 @@ public: int freqStart; // FFT bin index of the lowest frequency int framesPerTx; // number of frames to transmit a single chunk of data int bytesPerTx; // number of bytes in a chunk of data + int extra; // 2 if this is a mono-tone protocol, 1 otherwise int nDataBitsPerTx() const { return 8*bytesPerTx; } }; @@ -342,15 +346,18 @@ public: static const TxProtocols & getTxProtocols() { static const TxProtocols kTxProtocols { - { GGWAVE_TX_PROTOCOL_AUDIBLE_NORMAL, { "Normal", 40, 9, 3, } }, - { GGWAVE_TX_PROTOCOL_AUDIBLE_FAST, { "Fast", 40, 6, 3, } }, - { GGWAVE_TX_PROTOCOL_AUDIBLE_FASTEST, { "Fastest", 40, 3, 3, } }, - { GGWAVE_TX_PROTOCOL_ULTRASOUND_NORMAL, { "[U] Normal", 320, 9, 3, } }, - { GGWAVE_TX_PROTOCOL_ULTRASOUND_FAST, { "[U] Fast", 320, 6, 3, } }, - { GGWAVE_TX_PROTOCOL_ULTRASOUND_FASTEST, { "[U] Fastest", 320, 3, 3, } }, - { GGWAVE_TX_PROTOCOL_DT_NORMAL, { "[DT] Normal", 24, 9, 1, } }, - { GGWAVE_TX_PROTOCOL_DT_FAST, { "[DT] Fast", 24, 6, 1, } }, - { GGWAVE_TX_PROTOCOL_DT_FASTEST, { "[DT] Fastest", 24, 3, 1, } }, + { GGWAVE_TX_PROTOCOL_AUDIBLE_NORMAL, { "Normal", 40, 9, 3, 1, } }, + { GGWAVE_TX_PROTOCOL_AUDIBLE_FAST, { "Fast", 40, 6, 3, 1, } }, + { GGWAVE_TX_PROTOCOL_AUDIBLE_FASTEST, { "Fastest", 40, 3, 3, 1, } }, + { GGWAVE_TX_PROTOCOL_ULTRASOUND_NORMAL, { "[U] Normal", 320, 9, 3, 1, } }, + { GGWAVE_TX_PROTOCOL_ULTRASOUND_FAST, { "[U] Fast", 320, 6, 3, 1, } }, + { GGWAVE_TX_PROTOCOL_ULTRASOUND_FASTEST, { "[U] Fastest", 320, 3, 3, 1, } }, + { GGWAVE_TX_PROTOCOL_DT_NORMAL, { "[DT] Normal", 24, 9, 1, 1, } }, + { GGWAVE_TX_PROTOCOL_DT_FAST, { "[DT] Fast", 24, 6, 1, 1, } }, + { GGWAVE_TX_PROTOCOL_DT_FASTEST, { "[DT] Fastest", 24, 3, 1, 1, } }, + { GGWAVE_TX_PROTOCOL_MT_NORMAL, { "[MT] Normal", 24, 9, 1, 2, } }, + { GGWAVE_TX_PROTOCOL_MT_FAST, { "[MT] Fast", 24, 6, 1, 2, } }, + { GGWAVE_TX_PROTOCOL_MT_FASTEST, { "[MT] Fastest", 24, 3, 1, 2, } }, }; return kTxProtocols; diff --git a/src/ggwave.cpp b/src/ggwave.cpp index e4fc339..6168fdc 100644 --- a/src/ggwave.cpp +++ b/src/ggwave.cpp @@ -520,7 +520,8 @@ GGWave::GGWave(const Parameters & parameters) : const int totalLength = m_payloadLength + getECCBytesForLength(m_payloadLength); const int totalTxs = (totalLength + minBytesPerTx() - 1)/minBytesPerTx(); - m_rx->spectrumHistoryFixed.resize(totalTxs*maxFramesPerTx()); + // TODO: factor of 2 due to Mono-tone protocols + m_rx->spectrumHistoryFixed.resize(2*totalTxs*maxFramesPerTx()); m_rx->detectedBins.resize(2*totalLength); m_rx->detectedTones.resize(2*16*maxBytesPerTx()); } else { @@ -618,6 +619,11 @@ bool GGWave::init(int dataSize, const char * dataBuffer, const TxProtocol & txPr return false; } + if (txProtocol.extra == 2 && m_isFixedPayloadLength == false) { + ggprintf("Mono-tone protocols with variable length are not supported\n"); + return false; + } + m_tx->txProtocol = txProtocol; m_tx->txDataLength = dataSize; m_tx->sendVolume = ((double)(volume))/100.0f; @@ -685,10 +691,10 @@ uint32_t GGWave::encodeSize_samples() const { // note : +1 extra sample in order to overestimate the buffer size samplesPerFrameOut = m_resampler->resample(factor, m_samplesPerFrame, m_tx->outputBlock.data(), nullptr) + 1; } - int nECCBytesPerTx = getECCBytesForLength(m_tx->txDataLength); - int sendDataLength = m_tx->txDataLength + m_encodedDataOffset; - int totalBytes = sendDataLength + nECCBytesPerTx; - int totalDataFrames = ((totalBytes + m_tx->txProtocol.bytesPerTx - 1)/m_tx->txProtocol.bytesPerTx)*m_tx->txProtocol.framesPerTx; + const int nECCBytesPerTx = getECCBytesForLength(m_tx->txDataLength); + const int sendDataLength = m_tx->txDataLength + m_encodedDataOffset; + const int totalBytes = sendDataLength + nECCBytesPerTx; + const int totalDataFrames = m_tx->txProtocol.extra*((totalBytes + m_tx->txProtocol.bytesPerTx - 1)/m_tx->txProtocol.bytesPerTx)*m_tx->txProtocol.framesPerTx; return ( m_nMarkerFrames + totalDataFrames + m_nMarkerFrames @@ -703,7 +709,7 @@ bool GGWave::encode(const CBWaveformOut & cbWaveformOut) { const int nECCBytesPerTx = getECCBytesForLength(m_tx->txDataLength); const int sendDataLength = m_tx->txDataLength + m_encodedDataOffset; const int totalBytes = sendDataLength + nECCBytesPerTx; - const int totalDataFrames = ((totalBytes + m_tx->txProtocol.bytesPerTx - 1)/m_tx->txProtocol.bytesPerTx)*m_tx->txProtocol.framesPerTx; + const int totalDataFrames = m_tx->txProtocol.extra*((totalBytes + m_tx->txProtocol.bytesPerTx - 1)/m_tx->txProtocol.bytesPerTx)*m_tx->txProtocol.framesPerTx; if (m_isFixedPayloadLength == false) { RS::ReedSolomon rsLength(1, m_encodedDataOffset - 1, m_workRSLength.data()); @@ -741,13 +747,23 @@ bool GGWave::encode(const CBWaveformOut & cbWaveformOut) { std::fill(m_tx->dataBits.begin(), m_tx->dataBits.end(), 0); for (int j = 0; j < m_tx->txProtocol.bytesPerTx; ++j) { - { - uint8_t d = m_dataEncoded[dataOffset + j] & 15; - m_tx->dataBits[(2*j + 0)*16 + d] = 1; - } - { - uint8_t d = m_dataEncoded[dataOffset + j] & 240; - m_tx->dataBits[(2*j + 1)*16 + (d >> 4)] = 1; + if (m_tx->txProtocol.extra == 1) { + { + uint8_t d = m_dataEncoded[dataOffset + j] & 15; + m_tx->dataBits[(2*j + 0)*16 + d] = 1; + } + { + uint8_t d = m_dataEncoded[dataOffset + j] & 240; + m_tx->dataBits[(2*j + 1)*16 + (d >> 4)] = 1; + } + } else { + if (dataOffset % m_tx->txProtocol.extra == 0) { + uint8_t d = m_dataEncoded[dataOffset/m_tx->txProtocol.extra + j] & 15; + m_tx->dataBits[(2*j + 0)*16 + d] = 1; + } else { + uint8_t d = m_dataEncoded[dataOffset/m_tx->txProtocol.extra + j] & 240; + m_tx->dataBits[(2*j + 0)*16 + (d >> 4)] = 1; + } } } @@ -843,13 +859,23 @@ bool GGWave::encode(const CBWaveformOut & cbWaveformOut) { std::fill(m_tx->dataBits.begin(), m_tx->dataBits.end(), 0); for (int j = 0; j < m_tx->txProtocol.bytesPerTx; ++j) { - { - uint8_t d = m_dataEncoded[dataOffset + j] & 15; - m_tx->dataBits[(2*j + 0)*16 + d] = 1; - } - { - uint8_t d = m_dataEncoded[dataOffset + j] & 240; - m_tx->dataBits[(2*j + 1)*16 + (d >> 4)] = 1; + if (m_tx->txProtocol.extra == 1) { + { + uint8_t d = m_dataEncoded[dataOffset + j] & 15; + m_tx->dataBits[(2*j + 0)*16 + d] = 1; + } + { + uint8_t d = m_dataEncoded[dataOffset + j] & 240; + m_tx->dataBits[(2*j + 1)*16 + (d >> 4)] = 1; + } + } else { + if (dataOffset % m_tx->txProtocol.extra == 0) { + uint8_t d = m_dataEncoded[dataOffset/m_tx->txProtocol.extra + j] & 15; + m_tx->dataBits[(2*j + 0)*16 + d] = 1; + } else { + uint8_t d = m_dataEncoded[dataOffset/m_tx->txProtocol.extra + j] & 240; + m_tx->dataBits[(2*j + 0)*16 + (d >> 4)] = 1; + } } } @@ -1406,6 +1432,11 @@ void GGWave::decode_variable() { const auto & rxProtocolId = rxProtocolPair.first; const auto & rxProtocol = rxProtocolPair.second; + // skip Rx protocol if it is mono-tone + if (rxProtocol.extra == 2) { + continue; + } + // skip Rx protocol if start frequency is different from detected one if (rxProtocol.freqStart != m_rx->markerFreqStart) { continue; @@ -1664,13 +1695,14 @@ void GGWave::decode_fixed() { const int binStart = rxProtocol.freqStart; const int binDelta = 16; + const int binOffset = rxProtocol.extra == 1 ? binDelta : 0; if (binStart > m_samplesPerFrame) { continue; } const int totalLength = m_payloadLength + getECCBytesForLength(m_payloadLength); - const int totalTxs = (totalLength + rxProtocol.bytesPerTx - 1)/rxProtocol.bytesPerTx; + const int totalTxs = rxProtocol.extra*((totalLength + rxProtocol.bytesPerTx - 1)/rxProtocol.bytesPerTx); int historyStartId = m_rx->historyIdFixed - totalTxs*rxProtocol.framesPerTx; if (historyStartId < 0) { @@ -1685,7 +1717,9 @@ void GGWave::decode_fixed() { bool detectedSignal = true; for (int k = 0; k < totalTxs; ++k) { - std::fill(m_rx->detectedTones.begin(), m_rx->detectedTones.begin() + 16*nTones, 0); + if (k % rxProtocol.extra == 0) { + std::fill(m_rx->detectedTones.begin(), m_rx->detectedTones.begin() + 16*nTones, 0); + } for (int i = 0; i < rxProtocol.framesPerTx; ++i) { int historyId = historyStartId + k*rxProtocol.framesPerTx + i; @@ -1711,7 +1745,7 @@ void GGWave::decode_fixed() { } { - const auto & v = m_rx->spectrumHistoryFixed[historyId][binStart + 2*j*binDelta + binDelta + b]; + const auto & v = m_rx->spectrumHistoryFixed[historyId][binStart + 2*j*binDelta + binOffset + b]; if (f1max <= v) { f1max = v; @@ -1720,23 +1754,25 @@ void GGWave::decode_fixed() { } } - m_rx->detectedTones[(2*j + 0)*16 + f0bin]++; - m_rx->detectedTones[(2*j + 1)*16 + f1bin]++; + if ((k + 0)%rxProtocol.extra == 0) m_rx->detectedTones[(2*j + 0)*16 + f0bin]++; + if ((k + 1)%rxProtocol.extra == 0) m_rx->detectedTones[(2*j + 1)*16 + f1bin]++; } } + if (rxProtocol.extra > 1 && (k % rxProtocol.extra == 0)) continue; + int txNeeded = 0; int txDetected = 0; for (int j = 0; j < rxProtocol.bytesPerTx; ++j) { - if (k*rxProtocol.bytesPerTx + j >= totalLength) break; + if ((k/rxProtocol.extra)*rxProtocol.bytesPerTx + j >= totalLength) break; txNeeded += 2; for (int b = 0; b < 16; ++b) { if (m_rx->detectedTones[(2*j + 0)*16 + b] > rxProtocol.framesPerTx/2) { - m_rx->detectedBins[2*(k*rxProtocol.bytesPerTx + j) + 0] = b; + m_rx->detectedBins[2*((k/rxProtocol.extra)*rxProtocol.bytesPerTx + j) + 0] = b; txDetected++; } if (m_rx->detectedTones[(2*j + 1)*16 + b] > rxProtocol.framesPerTx/2) { - m_rx->detectedBins[2*(k*rxProtocol.bytesPerTx + j) + 1] = b; + m_rx->detectedBins[2*((k/rxProtocol.extra)*rxProtocol.bytesPerTx + j) + 1] = b; txDetected++; } } @@ -1759,6 +1795,7 @@ void GGWave::decode_fixed() { if (rsData.Decode(m_dataEncoded.data(), m_rx->rxData.data()) == 0) { if (m_rx->rxData[0] != 0) { + ggprintf("Decoded length = %d, protocol = '%s' (%d)\n", m_rx->rxData[0], rxProtocol.name, rxProtocolId); ggprintf("Received sound data successfully: '%s'\n", m_rx->rxData.data()); isValid = true; diff --git a/tests/test-ggwave.cpp b/tests/test-ggwave.cpp index 058fa36..83d4ff3 100644 --- a/tests/test-ggwave.cpp +++ b/tests/test-ggwave.cpp @@ -279,6 +279,11 @@ int main(int argc, char ** argv) { printf("Testing: protocol = %s, in = %d, out = %d\n", txProtocol.second.name, formatInp, formatOut); for (int length = 1; length <= (int) payload.size(); ++length) { + // mono-tone protocols with variable length are not supported + if (txProtocol.second.extra == 2) { + break; + } + // variable payload length { auto parameters = GGWave::getDefaultParameters();