remove-stl : prepare migration to ggvector

This commit is contained in:
Georgi Gerganov
2022-06-07 21:22:54 +03:00
parent 60c4bb950b
commit c8d6d6df3c
4 changed files with 304 additions and 239 deletions

View File

@@ -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 {

View File

@@ -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;

View File

@@ -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 <assert.h>
template <typename T>
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 <typename T>
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<T> operator[](int i) {
return ggvector<T>(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 <stdint.h>
#include <stdio.h>
#include <vector>
@@ -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<ToneData>;
using Tones = std::vector<TonesPerFrame>;
// 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<Tone>;
using Amplitude = std::vector<float>;
using AmplitudeI16 = std::vector<int16_t>;
@@ -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;

View File

@@ -32,7 +32,7 @@
namespace {
FILE * g_fptr = stderr;
std::vector<GGWave *> 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 <typename T>
void ggalloc(std::vector<T> & v, int n, void * buf, int & bufSize) {
if (buf == nullptr) {
bufSize += n*sizeof(T);
return;
}
v.resize(n);
bufSize += n*sizeof(T);
}
template <typename T>
void ggalloc(std::vector<std::vector<T>> & 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 <typename T>
void ggalloc(ggvector<T> & v, int n, void * buf, int & bufSize) {
if (buf == nullptr) {
bufSize += n*sizeof(T);
return;
}
v = ggvector<T>((T *)((char *) buf + bufSize), n);
bufSize += n*sizeof(T);
}
template <typename T>
void ggalloc(ggmatrix<T> & v, int n, int m, void * buf, int & bufSize) {
if (buf == nullptr) {
bufSize += n*m*sizeof(T);
return;
}
v = ggmatrix<T>((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;
}