diff --git a/.idea/discord.xml b/.idea/discord.xml
index 30bab2a..d8e9561 100644
--- a/.idea/discord.xml
+++ b/.idea/discord.xml
@@ -1,7 +1,7 @@
-
+
\ No newline at end of file
diff --git a/.idea/editor.xml b/.idea/editor.xml
index 1798069..ad8bb34 100644
--- a/.idea/editor.xml
+++ b/.idea/editor.xml
@@ -27,25 +27,25 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 80246fa..9b89aed 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -30,5 +30,6 @@ add_executable(audiocoder
decoder.c
decoder.h
goertzel.c
- goertzel.h)
+ goertzel.h
+ protocol.h)
target_link_libraries(audiocoder SDL2 SDL2_ttf m)
\ No newline at end of file
diff --git a/decoder.c b/decoder.c
index b54017a..06352e1 100644
--- a/decoder.c
+++ b/decoder.c
@@ -12,30 +12,24 @@
#include
#include "texteditor.h"
+#include "modem_settings.h"
+#include "packet.h"
+#include "protocol.h"
Decoder decoder;
float tone_energy(
Decoder *dec,
int index) {
- return goertzel_energy(
- &dec->tones[index]
- );
-}
-
-static void decoder_reset_symbol_filters(Decoder *dec) {
- const int tones = dec->bitCount * 2 + 2;
-
- for (int i = 0; i < tones; ++i) {
- dec->tones[i].s1 = 0.0f;
- dec->tones[i].s2 = 0.0f;
- }
+ // Return the current accumulated Goertzel energy without clearing state.
+ Goertzel *g = &dec->tones[index];
+ return g->s1 * g->s1 + g->s2 * g->s2 - g->coeff * g->s1 * g->s2;
}
static void decoder_history_push(Decoder *dec, float sample) {
dec->sampleHistory[dec->sampleHistoryWrite] = sample;
dec->sampleHistoryWrite =
- (dec->sampleHistoryWrite + 1) % DECODER_SAMPLE_HISTORY;
+ (dec->sampleHistoryWrite + 1) % DECODER_SAMPLE_HISTORY;
if (dec->sampleHistoryCount < DECODER_SAMPLE_HISTORY)
dec->sampleHistoryCount++;
@@ -43,308 +37,147 @@ static void decoder_history_push(Decoder *dec, float sample) {
dec->totalSamples++;
}
-static bool decoder_history_get(
- const Decoder *dec,
- uint64_t sampleIndex,
- float *sample)
-{
- if (sampleIndex >= dec->totalSamples)
+static bool decoder_history_get(const Decoder *dec, uint64_t sampleIndex, float *sample) {
+ if (sampleIndex >= dec->totalSamples) {
return false;
+ }
- uint64_t oldest =
- dec->totalSamples - dec->sampleHistoryCount;
+ uint64_t oldest = dec->totalSamples - dec->sampleHistoryCount;
- if (sampleIndex < oldest)
+ if (sampleIndex < oldest) {
return false;
+ }
uint32_t start =
- (dec->sampleHistoryWrite + DECODER_SAMPLE_HISTORY - dec->sampleHistoryCount)
- % DECODER_SAMPLE_HISTORY;
-
- uint32_t rel =
- (uint32_t)(sampleIndex - oldest);
-
- *sample =
- dec->sampleHistory[(start + rel) % DECODER_SAMPLE_HISTORY];
+ (dec->sampleHistoryWrite + DECODER_SAMPLE_HISTORY - dec->sampleHistoryCount)
+ % DECODER_SAMPLE_HISTORY;
+ uint32_t rel = (uint32_t) (sampleIndex - oldest);
+ *sample = dec->sampleHistory[(start + rel) % DECODER_SAMPLE_HISTORY];
return true;
}
-static uint8_t decode_symbol_from_work(
- Goertzel *tones,
- uint8_t bitCount)
-{
- uint8_t byte = 0;
- int index = 0;
-
- for (int bit = 0; bit < bitCount; ++bit) {
- float e0 = goertzel_energy(&tones[index++]);
- float e1 = goertzel_energy(&tones[index++]);
-
- if (e1 > e0)
- byte |= 1u << bit;
- }
-
- return byte;
-}
-
-static bool decode_symbol_at(
- Decoder *dec,
- uint64_t sampleIndex,
- uint8_t *out)
-{
- const int tones = dec->bitCount * 2 + 2;
- Goertzel work[MAX_BITS * 2 + 2];
-
- memcpy(work, dec->tones, sizeof(work));
-
- for (int i = 0; i < tones; ++i) {
- work[i].s1 = 0.0f;
- work[i].s2 = 0.0f;
- }
-
- for (uint32_t s = 0; s < dec->symbolSamples; ++s) {
- float sample;
- if (!decoder_history_get(dec, sampleIndex + s, &sample))
- return false;
-
- for (int t = 0; t < tones; ++t)
- goertzel_add(&work[t], sample);
- }
-
- *out = decode_symbol_from_work(work, dec->bitCount);
- return true;
-}
-
-static void decoder_seed_live_symbol(
- Decoder *dec,
- uint64_t startSample)
-{
- const int tones = dec->bitCount * 2 + 2;
-
- decoder_reset_symbol_filters(dec);
- dec->sampleCount = 0;
-
- for (uint64_t index = startSample; index < dec->totalSamples; ++index) {
- float sample;
- if (!decoder_history_get(dec, index, &sample))
- break;
-
- for (int t = 0; t < tones; ++t)
- goertzel_add(&dec->tones[t], sample);
-
- dec->sampleCount++;
- }
-}
-
-static bool try_phase_search(Decoder *dec) {
- const uint32_t phaseStep = 16;
- const uint32_t syncSymbols = (uint32_t)sizeof(syncBytes);
- const uint64_t syncSamples =
- (uint64_t)syncSymbols * (uint64_t)dec->symbolSamples;
- const uint64_t oldest =
- dec->totalSamples - dec->sampleHistoryCount;
-
- int bestScore = -1;
- int bestPhase = 0;
- uint64_t bestStart = 0;
-
- if (dec->sampleHistoryCount < syncSamples + dec->symbolSamples)
- return false;
-
- for (uint32_t phase = 0; phase < dec->symbolSamples; phase += phaseStep) {
- int64_t start =
- (int64_t)dec->totalSamples -
- (int64_t)syncSamples -
- (int64_t)phase;
-
- if (start < (int64_t)oldest)
- continue;
-
- int score = 0;
-
- for (size_t i = 0; i < sizeof(syncBytes); ++i) {
- uint8_t byte;
- if (!decode_symbol_at(
- dec,
- (uint64_t)start +
- (uint64_t)i * dec->symbolSamples,
- &byte)) {
- score = -1;
- break;
- }
-
- if (byte != syncBytes[i])
- break;
-
- score++;
- }
-
- if (score > bestScore) {
- bestScore = score;
- bestPhase = (int)phase;
- bestStart = (uint64_t)start;
- }
- }
-
- dec->bestSyncScore = bestScore;
- dec->bestSyncPhase = bestPhase;
-
- if (bestScore != (int)sizeof(syncBytes)) {
- printf("PHASE SEARCH FAILED bestScore=%d (need %zu)\n", bestScore, sizeof(syncBytes));
- return false;
- }
-
- uint64_t dataStart =
- bestStart + syncSamples;
-
- printf("SYNC ALIGN phase=%d score=%d\n", bestPhase, bestScore);
- dec->phaseOffset = bestPhase;
- dec->phaseWait = 0;
- dec->phaseSearchReady = false;
- dec->rxState = RX_LOCKED;
- dec->locked = true;
- dec->bufferSize = 0;
- dec->clockHistory = 0;
- dec->clockCount = 0;
- decoder_seed_live_symbol(dec, dataStart);
- return true;
-}
-
-
void decoder_init(
Decoder *dec) {
memset(dec, 0, sizeof(*dec));
- dec->bitCount = 8;
+ dec->bitsPerSymbol = 3;
+ dec->toneCount = 8;
- dec->startFreq = 1000;
- dec->endFreq = 5000;
- dec->dataSpacing = 250;
+ dec->startFreq = DEFAULT_START_FREQ;
+ dec->endFreq = DEFAULT_END_FREQ;
+ uint16_t freqRange = dec->endFreq - dec->startFreq;
+ dec->dataSpacing =
+ freqRange /
+ (encoder.toneCount - 1);
+ dec->sampleRate = SAMPLE_RATE;
dec->symbolSamples =
- SAMPLE_RATE / 50;
+ SAMPLE_RATE / DEFAULT_SYMBOL_RATE;
dec->clock0 = goertzel_bin_freq(
- 6000,
- SAMPLE_RATE,
- dec->symbolSamples
-);
-
- dec->clock1 = goertzel_bin_freq(
- 6300,
- SAMPLE_RATE,
+ DEFAULT_CLOCK0_FREQ,
+ dec->sampleRate,
dec->symbolSamples
);
-
- /*
- float spacing =
- (float) (dec->endFreq - dec->startFreq)
- /
- (dec->bitCount * 2 - 1);
-
- */
+ dec->clock1 = goertzel_bin_freq(
+ DEFAULT_CLOCK1_FREQ,
+ dec->sampleRate,
+ dec->symbolSamples
+ );
decoder_rebuild_tones(dec);
}
-void decoder_rebuild_tones(
- Decoder *dec)
-{
- int index = 0;
+void decoder_rebuild_tones(Decoder *dec) {
+ dec->toneCount = 1 << dec->bitsPerSymbol;
- for (int i = 0; i < dec->bitCount; i++) {
- goertzel_init(
- &dec->tones[index++],
- dec->startFreq +
- dec->dataSpacing * (i * 2),
- SAMPLE_RATE,
+ printf("Decoder tones: %d\n", dec->toneCount);
+
+
+ // clock tones
+ goertzel_init(
+ &dec->tones[0],
+ dec->clock0,
+ dec->sampleRate,
+ dec->symbolSamples
+ );
+
+ goertzel_init(
+ &dec->tones[1],
+ dec->clock1,
+ dec->sampleRate,
+ dec->symbolSamples
+ );
+
+
+ // data tones
+ for (int i = 0; i < dec->toneCount; i++) {
+ uint16_t freq =
+ dec->startFreq +
+ i * dec->dataSpacing;
+
+
+ // MUST match encoder
+ freq = goertzel_bin_freq(
+ freq,
+ dec->sampleRate,
dec->symbolSamples
);
goertzel_init(
- &dec->tones[index++],
- dec->startFreq +
- dec->dataSpacing * (i * 2 + 1),
- SAMPLE_RATE,
+ &dec->tones[i + 2],
+ freq,
+ dec->sampleRate,
dec->symbolSamples
);
+
+
+ printf(
+ "Tone %d = %dHz\n",
+ i,
+ freq
+ );
}
- goertzel_init(
- &dec->tones[index++],
- dec->clock0,
- SAMPLE_RATE,
- dec->symbolSamples
+
+ memset(
+ dec->energies,
+ 0,
+ sizeof(dec->energies)
);
- goertzel_init(
- &dec->tones[index++],
- dec->clock1,
- SAMPLE_RATE,
- dec->symbolSamples
- );
- dec->sampleCount = 0;
- dec->sampleHistoryWrite = 0;
- dec->sampleHistoryCount = 0;
- dec->totalSamples = 0;
- dec->maxEnergy = 0.0f;
- dec->clockEnergy0 = 0.0f;
- dec->clockEnergy1 = 0.0f;
- dec->clockRatio = 0.0f;
- dec->bestSyncScore = 0;
- dec->bestSyncPhase = 0;
- dec->bufferSize = 0;
dec->rxState = RX_SEARCH;
- dec->phaseSearchReady = false;
- dec->phaseWait = 0;
- dec->phaseOffset = 0;
- dec->clockHistory = 0;
- dec->clockCount = 0;
- dec->goodClocks = 0;
- dec->searchOffset = 0;
- dec->locked = false;
- memset(dec->energies, 0, sizeof(dec->energies));
}
static int detect_clock(Decoder *dec) {
- float e0 = tone_energy(dec, 16);
- float e1 = tone_energy(dec, 17);
+ // Use instantaneous per-symbol energies (dec->energies) for fast detection
+ float e0 = dec->energies[0];
+ float e1 = dec->energies[1];
- float max = fmaxf(e0, e1);
- float min = fminf(e0, e1);
-
- dec->clockEnergy0 = e0;
- dec->clockEnergy1 = e1;
- dec->clockRatio = min > 0.0f ? max / min : 0.0f;
- dec->energies[16] = e0;
- dec->energies[17] = e1;
- dec->maxEnergy = fmaxf(dec->maxEnergy * 0.85f, max);
-
-
- /*
- Ignore silence/noise.
-
- Your real signal is around 0.1+
- noise is around 0.001-0.01
- */
- if (max < 0.001f)
+ // Simple silence check
+ float peak = fmaxf(e0, e1);
+ if (peak < 1e-7f)
return -1;
- return e1 > e0;
+ // Determine which clock tone is stronger with a small hysteresis
+ if (e1 > e0 * 1.05f)
+ return 1;
+ if (e0 > e1 * 1.05f)
+ return 0;
+
+ return -1; // uncertain
}
static void clock_search(
- Decoder *dec,
- int clock)
-{
- if(clock < 0)
- {
+ Decoder *dec,
+ int clock) {
+ if (clock < 0) {
dec->clockHistory = 0;
dec->clockCount = 0;
return;
@@ -352,21 +185,19 @@ static void clock_search(
dec->clockHistory =
- (dec->clockHistory << 1) |
- (clock & 1);
+ (dec->clockHistory << 1) |
+ (clock & 1);
- if(dec->clockCount < 8)
+ if (dec->clockCount < 8)
dec->clockCount++;
- if(dec->clockCount >= 8)
- {
+ if (dec->clockCount >= 8) {
uint8_t h = dec->clockHistory & 0xff;
- if(h == 0x55 || h == 0xAA)
- {
+ if (h == 0x55 || h == 0xAA) {
printf("CLOCK LOCK pattern=%02X\n", h);
dec->goodClocks = 8;
dec->clockHistory = 0;
@@ -378,112 +209,293 @@ static void clock_search(
dec->goodClocks = 0;
}
}
-static uint8_t decode_symbol(Decoder *dec) {
- uint8_t byte = 0;
- int index = 0;
- for (int bit = 0; bit < dec->bitCount; ++bit) {
- float e0 = tone_energy(dec, index);
- float e1 = tone_energy(dec, index + 1);
- float max = fmaxf(e0, e1);
+static void update_all_energies(Decoder *dec) {
+ const int tones = dec->toneCount + 2;
- dec->energies[index++] = e0;
- dec->energies[index++] = e1;
- dec->maxEnergy = fmaxf(dec->maxEnergy * 0.85f, max);
+ uint64_t oldest = dec->totalSamples - dec->sampleHistoryCount;
+ if (dec->totalSamples < dec->symbolSamples)
+ return; // not enough samples yet
- if (e1 > e0)
- byte |= 1u << bit;
+ uint64_t start = dec->totalSamples - dec->symbolSamples;
+ if (start < oldest) start = oldest;
+
+ Goertzel work[MAX_FSK_TONES + 2];
+ memcpy(work, dec->tones, sizeof(Goertzel) * tones);
+ for (int i = 0; i < tones; ++i) {
+ work[i].s1 = 0.0f;
+ work[i].s2 = 0.0f;
}
- dec->lastDecodedByte = byte;
- return byte;
+ for (uint32_t s = 0; s < dec->symbolSamples; ++s) {
+ float sample;
+ if (!decoder_history_get(dec, start + s, &sample)) { sample = 0.0f; }
+ for (int t = 0; t < tones; ++t)
+ goertzel_add(&work[t], sample);
+ }
+
+ float maxE = 0.0f;
+ for (int t = 0; t < tones; ++t) {
+ float e = goertzel_energy(&work[t]);
+ // Instantaneous per-symbol energy
+ dec->energies[t] = e;
+ if (t == 0) dec->clockEnergy0 = e;
+ if (t == 1) dec->clockEnergy1 = e;
+ if (e > maxE) maxE = e;
+ }
+
+ // Faster decay so visualization follows symbol changes
+ dec->maxEnergy = fmaxf(dec->maxEnergy * 0.8f, maxE);
+
+ // Maintain a short visual smoothing for clocks so bars don't flicker
+ if (!isfinite(dec->clockEnergy0)) dec->clockEnergy0 = dec->energies[0];
+ if (!isfinite(dec->clockEnergy1)) dec->clockEnergy1 = dec->energies[1];
+ dec->clockEnergy0 = dec->clockEnergy0 * 0.7f + dec->energies[0] * 0.3f;
+ dec->clockEnergy1 = dec->clockEnergy1 * 0.7f + dec->energies[1] * 0.3f;
+
+#ifdef DEBUG_ENERGIES
+ int show = dec->toneCount + 2;
+ int upto = show < 12 ? show : 12;
+ printf("ENERGIES(start=%llu):", (unsigned long long) start);
+ for (int i = 0; i < upto; ++i) {
+ printf(" %d:%.6f", i, dec->energies[i]);
+ }
+ printf(" maxE=%.6f clk0=%.6f clk1=%.6f\n", dec->maxEnergy, dec->clockEnergy0, dec->clockEnergy1);
+#endif
+}
+
+static int decode_symbol(
+ Decoder *dec) {
+ float bestEnergy = 0;
+ int bestTone = 0;
+
+
+ /*
+ data tones start after clock tones
+ */
+ for (int i = 0; i < dec->toneCount; i++) {
+ float e =
+ dec->energies[i + 2];
+
+
+ if (e > bestEnergy) {
+ bestEnergy = e;
+ bestTone = i;
+ }
+ }
+
+
+ return bestTone;
+}
+
+bool decoder_push_byte(
+ Decoder *dec,
+ uint8_t b) {
+ if (dec->rxState == RX_MAGIC) {
+ dec->magicBuffer[dec->magicIndex++] = b;
+
+ printf("MAGIC %02X\n", b);
+
+ if (dec->magicIndex >= MAGIC_SIZE) {
+ if (memcmp(dec->magicBuffer,
+ PACKET_MAGIC,
+ MAGIC_SIZE) == 0) {
+ printf("MAGIC OK\n");
+
+ dec->rxState = RX_DATA;
+
+ dec->bufferSize = 0;
+ dec->currentByte = 0;
+ dec->bitIndex = 0;
+ }
+
+ printf("BAD MAGIC\n");
+
+ dec->magicIndex = 0;
+ dec->preambleCount = 0;
+ dec->rxState = RX_SEARCH;
+ }
+
+ return false;
+ }
+
+
+ if (dec->rxState == RX_DATA) {
+ if (dec->bufferSize < sizeof(dec->buffer)) {
+ dec->buffer[dec->bufferSize++] = b;
+ }
+
+ if(decoder_parse_packet(dec))
+ {
+ printf("PACKET OK\n");
+
+ decoder_reset_rx(dec);
+ }
+ }
+
+ return false;
+}
+
+static void decoder_push_symbol(
+ Decoder *dec,
+ unsigned symbol) {
+ for (int i = 0; i < dec->bitsPerSymbol; i++) {
+ int bit = (symbol >> i) & 1;
+
+ dec->currentByte |= bit << dec->bitIndex;
+
+ if (++dec->bitIndex == 8) {
+ printf("BYTE %02X\n", dec->currentByte);
+
+ decoder_push_byte(
+ dec,
+ dec->currentByte);
+
+ dec->currentByte = 0;
+ dec->bitIndex = 0;
+ }
+ }
+}
+
+static bool clock_valid(Decoder *dec) {
+ float e0 = dec->energies[0];
+ float e1 = dec->energies[1];
+
+ float peak = fmaxf(e0, e1);
+
+ if (peak < dec->maxEnergy * 0.10f)
+ return false;
+
+ float ratio = fmaxf(e0, e1) /
+ (fminf(e0, e1) + 1e-12f);
+
+ return ratio > 1.3f;
+}
+
+void decoder_reset_rx(Decoder *dec) {
+ dec->rxState = RX_SEARCH;
+ dec->locked = false;
+
+ dec->preambleCount = 0;
+ dec->magicIndex = 0;
+
+ dec->bufferSize = 0;
+ dec->currentByte = 0;
+ dec->bitIndex = 0;
+
+ dec->clockHistory = 0;
+ dec->clockCount = 0;
+ dec->goodClocks = 0;
}
void decoder_process(
Decoder *dec,
float *samples,
uint32_t count) {
- const int tones = dec->bitCount * 2 + 2;
+ const int tones = dec->toneCount + 2;
for (uint32_t i = 0; i < count; i++) {
decoder_history_push(dec, samples[i]);
- if (dec->rxState == RX_SEARCH || dec->rxState == RX_LOCKED) {
- for (int t = 0; t < tones; t++)
- goertzel_add(&dec->tones[t], samples[i]);
- }
+ // Always feed samples to Goertzel for continuous analysis
+ for (int t = 0; t < tones; t++)
+ goertzel_add(&dec->tones[t], samples[i]);
- if (dec->rxState == RX_ALIGN) {
- if (dec->phaseWait > 0)
- dec->phaseWait--;
+ dec->sampleCount += 1;
- if (dec->phaseWait == 0) {
- decoder_reset_symbol_filters(dec);
- dec->sampleCount = 0;
- dec->rxState = RX_LOCKED;
- printf("PHASE LOCKED offset=%d\n", dec->phaseOffset);
- }
-
- continue;
- }
-
- dec->sampleCount++;
-
- if (dec->sampleCount < dec->symbolSamples)
+ if (dec->sampleCount != dec->symbolSamples)
continue;
dec->sampleCount = 0;
switch (dec->rxState) {
case RX_SEARCH: {
+ // Update all energies for real-time visual feedback
+ update_all_energies(dec);
+
int clock = detect_clock(dec);
clock_search(dec, clock);
if (dec->goodClocks >= 8) {
- dec->searchOffset++;
- if (dec->searchOffset >= 4) {
- dec->searchOffset = 0;
- if (try_phase_search(dec)) {
- dec->phaseSearchReady = false;
- dec->sampleCount = 0;
- dec->clockHistory = 0;
- dec->clockCount = 0;
- dec->goodClocks = 0;
- } else {
- // Phase search failed, reset and try again
- dec->goodClocks = 0;
- dec->clockHistory = 0;
- dec->clockCount = 0;
- }
- }
- }
- break;
- }
+ printf("CLOCK LOCKED\n");
+
+ dec->rxState = RX_SYNC;
+ dec->locked = true;
- case RX_LOCKED: {
- int clock = detect_clock(dec);
- if (clock < 0) {
- printf("clock lost\n");
- dec->rxState = RX_SEARCH;
- dec->locked = false;
- dec->sampleCount = 0;
dec->clockHistory = 0;
dec->clockCount = 0;
+
dec->goodClocks = 0;
- dec->searchOffset = 0;
- decoder_reset_symbol_filters(dec);
- break;
}
- uint8_t b = decode_symbol(dec);
-
- if (dec->bufferSize < sizeof(dec->buffer))
- dec->buffer[dec->bufferSize++] = b;
-
- printf("DATA %02X\n", b);
+ // DON'T reset filters - keep energy visible
break;
}
+ case RX_SYNC: {
+ update_all_energies(dec);
+
+
+ int symbol = decode_symbol(dec);
+
+ if (clock_valid(dec)) {
+ dec->lostClockCount = 0;
+ printf("Symbol %d energy=%f\n", symbol, dec->energies[symbol + 2]);
+ } else {
+ if (++dec->lostClockCount > 8) {
+ printf("CLOCK LOST\n");
+
+ decoder_reset_rx(dec);
+
+ break;
+ }
+ }
+
+
+ if (symbol == (dec->preambleCount & 7)) {
+ if (dec->preambleCount < 255)
+ dec->preambleCount++;
+
+ if (dec->preambleCount >= 16) {
+ printf("PREAMBLE OK\n");
+
+ dec->rxState = RX_MAGIC;
+
+ dec->magicIndex = 0;
+ dec->currentByte = 0;
+ dec->bitIndex = 0;
+
+ break;
+ }
+ } else {
+ if (dec->preambleCount > 4)
+ dec->preambleCount -= 4;
+ else
+ dec->preambleCount = 0;
+ }
+
+ break;
+ }
+ case RX_MAGIC:
+ case RX_DATA: {
+ update_all_energies(dec);
+
+ if (!clock_valid(dec)) {
+ if (++dec->lostClockCount > 8) {
+ printf("CLOCK LOST\n");
+ decoder_reset_rx(dec);
+ break;
+ }
+ } else {
+ dec->lostClockCount = 0;
+ }
+
+ int symbol = decode_symbol(dec);
+
+ decoder_push_symbol(dec, symbol);
+
+ break;
+ }
}
}
}
@@ -494,18 +506,9 @@ void input_callback(
int len) {
Decoder *dec = userdata;
+ // stream contains audio captured from sound card input
float *samples = (float *) stream;
int count = len / sizeof(float);
-
- // Read from internal bus for loopback
- for (int i = 0; i < count; i++) {
- if (internalBusRead != internalBusWrite) {
- samples[i] = internalBusSamples[internalBusRead];
- internalBusRead = (internalBusRead + 1) % INTERNAL_BUFFER_SIZE;
- } else {
- samples[i] = 0.0f;
- }
- }
decoder_process(
dec,
@@ -526,7 +529,7 @@ void initAudioDecoder(SDL_Renderer *renderer) {
inSpec.freq = SAMPLE_RATE;
inSpec.format = AUDIO_F32SYS;
inSpec.channels = 1;
- inSpec.samples = 512;
+ inSpec.samples = 960; // Match symbolSamples (48000/50)
inSpec.callback = input_callback;
inSpec.userdata = &decoder;
@@ -538,7 +541,14 @@ void initAudioDecoder(SDL_Renderer *renderer) {
printf("Failed to open audio decoder: %s\n", SDL_GetError());
SDL_Quit();
}
- printf("Input device spec: freq=%d channels=%d samples=%d\n",
- realSpec.freq, realSpec.channels, realSpec.samples);
+ printf("Input device spec: freq=%d channels=%d samples=%d format=%d\n",
+ realSpec.freq, realSpec.channels, realSpec.samples, realSpec.format);
+
+ // Update decoder sample rate and symbolSamples based on actual device
+ decoder.sampleRate = realSpec.freq;
+ decoder.symbolSamples = realSpec.freq / DEFAULT_SYMBOL_RATE;
+ // Rebuild tones to match actual sample rate
+ decoder_rebuild_tones(&decoder);
+
SDL_PauseAudioDevice(inputDev, 0);
}
diff --git a/decoder.h b/decoder.h
index 9aa8387..b3fa883 100644
--- a/decoder.h
+++ b/decoder.h
@@ -5,25 +5,36 @@
#ifndef AUDIOCODER_DECODER_H
#define AUDIOCODER_DECODER_H
-#define MAX_BITS 16
-#define PREAMBLE_SYMBOLS 128
#define DECODER_SAMPLE_HISTORY 262144
+#define MAX_FSK_TONES 32
#include
#include
#include
#include "goertzel.h"
-typedef enum {
+typedef enum
+{
RX_SEARCH,
- RX_ALIGN,
RX_SYNC,
- RX_LOCKED
+ RX_MAGIC,
+ RX_DATA
} RxState;
+typedef enum
+{
+ PKT_WAIT_PREAMBLE,
+ PKT_WAIT_MAGIC,
+ PKT_WAIT_LENGTH,
+ PKT_WAIT_DATA,
+ PKT_WAIT_CRC1,
+ PKT_WAIT_CRC2
+ } PacketState;
+
typedef struct {
- uint8_t bitCount;
+ uint8_t bitsPerSymbol;
+ uint8_t toneCount;
uint16_t startFreq;
uint16_t endFreq;
@@ -32,9 +43,16 @@ typedef struct {
uint16_t clock0;
uint16_t clock1;
+ uint8_t magicBuffer[16];
+ uint32_t magicIndex;
+ uint32_t preambleCount;
+
+ uint8_t lostClockCount;
+ uint8_t goodClockCount;
+
uint32_t symbolSamples;
-
+ uint32_t sampleRate;
uint32_t sampleCount;
@@ -43,7 +61,9 @@ typedef struct {
uint32_t sampleHistoryCount;
uint64_t totalSamples;
- Goertzel tones[MAX_BITS * 2 + 2];
+ Goertzel tones[MAX_FSK_TONES + 2];
+
+ float energies[MAX_FSK_TONES + 2];
uint8_t currentByte;
uint8_t bitIndex;
@@ -55,7 +75,6 @@ typedef struct {
bool lastClock;
- float energies[MAX_BITS * 2 + 2];
float maxEnergy;
float clockEnergy0;
float clockEnergy1;
@@ -65,6 +84,8 @@ typedef struct {
int bestSyncPhase;
RxState rxState;
+ PacketState packetState;
+
bool expectedClock;
float lastClockEnergy;
@@ -116,5 +137,6 @@ extern volatile uint32_t internalBusWrite;
extern volatile uint32_t internalBusRead;
void initAudioDecoder(SDL_Renderer *renderer);
+void decoder_reset_rx(Decoder *dec);
#endif //AUDIOCODER_DECODER_H
diff --git a/encoder.c b/encoder.c
index be0e565..396a293 100644
--- a/encoder.c
+++ b/encoder.c
@@ -9,72 +9,93 @@
#include
#include "goertzel.h"
+#include "modem_settings.h"
+#include "packet.h"
SDL_AudioDeviceID dev;
AudioData encodedAudioData;
Encoder encoder;
-// Internal audio bus
-float internalBusSamples[INTERNAL_BUFFER_SIZE] = {0};
-volatile uint32_t internalBusWrite = 0;
-volatile uint32_t internalBusRead = 0;
-#define PREAMBLE_SYMBOLS 128
-
-const uint8_t syncBytes[4] =
+const uint8_t syncSymbols[] =
{
- 'B',
- 'R',
- 'N',
- 'Q'
+ 2,0,
+ 2,2,
+ 6,2,
+ 1,2
};
-static void encoder_set_bit(
- AudioData *audio,
- Encoder *enc,
- unsigned bitIndex,
- int bit)
+static void encoder_set_symbol(
+ AudioData *audio,
+ Encoder *enc,
+ unsigned symbol)
{
/*
- float spacing =
- (float)(enc->endFreq - enc->startFreq) /
- (enc->bitCount * 2 - 1);
- */
+ symbol:
+ 0 ... number of FSK tones-1
+ */
uint16_t freq =
enc->startFreq +
- enc->dataSpacing * (bitIndex * 2 + bit);
+ symbol * enc->dataSpacing;
+
freq = goertzel_bin_freq(
- freq,
- SAMPLE_RATE,
- enc->symbolSamples
-);
+ freq,
+ enc->sampleRate,
+ enc->symbolSamples
+ );
+
SynthVoice *v =
- &audio->synthVoices[bitIndex + 1];
+ &audio->synthVoices[DATA_VOICE];
+
v->frequency = freq;
+
v->phaseStep =
- ((uint64_t)freq << 32) / SAMPLE_RATE;
+ ((uint64_t)freq << 32) /
+ enc->sampleRate;
+
v->volume = 255;
}
-static int encoder_get_bit(Encoder *enc) {
- if (enc->byteIndex >= enc->bufferSize)
- return -1;
+static int encoder_get_symbol(
+ Encoder *enc)
+{
+ unsigned symbol=0;
- int bit = (enc->buffer[enc->byteIndex] >> enc->bitIndex) & 1;
- enc->bitIndex++;
+ for(unsigned i=0;
+ ibitsPerSymbol;
+ i++)
+ {
- if (enc->bitIndex == 8) {
- enc->bitIndex = 0;
- enc->byteIndex++;
+ if(enc->packetIndex >= enc->packetSize)
+ return -1;
+
+
+ int bit =
+ (enc->packet[enc->packetIndex]
+ >>enc->bitIndex)&1;
+
+
+ symbol |= bit<bitIndex++;
+
+
+ if(enc->bitIndex==8)
+ {
+ enc->bitIndex=0;
+ enc->packetIndex++;
+ }
}
- return bit;
+
+ return symbol;
}
static void encoder_clock(
@@ -94,109 +115,91 @@ static void encoder_clock(
clock->frequency = freq;
clock->phaseStep =
((uint64_t)freq << 32) /
- SAMPLE_RATE;
+ (enc->sampleRate ? enc->sampleRate : SAMPLE_RATE);
clock->volume = 255;
+
+ // Debug: print clock toggle
+ //printf("ENCODER CLOCK: toggled to %u Hz (state=%d)\n", freq, enc->clockState);
}
void encoder_next_symbol(
- Encoder *enc,
- AudioData *audio)
+ Encoder *enc,
+ AudioData *audio)
{
- encoder_clock(enc, audio);
+
+ encoder_clock(enc,audio);
+
switch(enc->state)
{
+
case TX_PREAMBLE:
+ {
+ unsigned symbol =
+ enc->preambleSymbols %
+ enc->toneCount;
- /* transmit all-zero symbol */
- for(unsigned i=0;ibitCount;i++)
- encoder_set_bit(audio,enc,i,0);
+ encoder_set_symbol(
+ audio,
+ enc,
+ symbol);
+
enc->preambleSymbols++;
- if(enc->preambleSymbols >= PREAMBLE_SYMBOLS)
+
+ if(enc->preambleSymbols>=PREAMBLE_SYMBOLS)
{
- enc->syncIndex = 0;
- enc->bitIndex = 0;
- enc->state = TX_SYNC;
- }
+ enc->packetIndex=0;
+ enc->bitIndex=0;
- break;
-
- case TX_SYNC:
- {
- uint8_t byte =
- syncBytes[enc->syncIndex];
-
- for(unsigned i=0;ibitCount;i++)
- {
- encoder_set_bit(
- audio,
- enc,
- i,
- (byte >> i) & 1);
- }
-
- enc->syncIndex++;
-
- if(enc->syncIndex ==
- sizeof(syncBytes))
- {
- enc->state = TX_DATA;
- enc->byteIndex = 0;
- enc->bitIndex = 0;
+ enc->state=TX_PACKET;
}
break;
}
- case TX_DATA:
+
+ case TX_PACKET:
{
- int finished = 0;
+ int symbol =
+ encoder_get_symbol(enc);
- for(unsigned i=0;ibitCount;i++)
+
+ if(symbol<0)
{
- int bit =
- encoder_get_bit(enc);
-
- if(bit < 0)
- {
- bit = 0;
- finished++;
- }
-
- encoder_set_bit(
- audio,
- enc,
- i,
- bit);
+ enc->state=TX_END;
+ break;
}
- if(finished ==
- enc->bitCount)
- {
- enc->state = TX_END;
- }
+
+ encoder_set_symbol(
+ audio,
+ enc,
+ symbol);
break;
}
+
case TX_END:
-
stopEncoder(enc);
break;
+
default:
break;
}
- enc->samplesRemaining =
+
+ enc->samplesRemaining=
enc->symbolSamples;
}
void startEncoder(Encoder *enc)
{
+ encoder_build_packet(enc, enc->buffer, enc->bufferSize);
enc->state = TX_PREAMBLE;
enc->transmitting = true;
@@ -214,7 +217,7 @@ void stopEncoder(Encoder *enc)
enc->transmitting=false;
enc->state=TX_IDLE;
- for(int i=0;isamplesRemaining == 0)
+ if (enc->samplesRemaining == 0) {
encoder_next_symbol(enc, audio);
+ }
enc->samplesRemaining--;
float mix = 0.0f;
int active = 0;
- for (unsigned v = 0; v < enc->bitCount + 1; v++) {
+ for(unsigned v=0; v<2; v++) {
SynthVoice *voice = &audio->synthVoices[v];
if (voice->volume == 0)
@@ -261,10 +265,6 @@ void audio_callback(void *userdata, Uint8 *stream, int len) {
float output = active ? mix / (active + 1) : 0.0f;
out[i] = output;
-
- // Write to internal bus for loopback
- internalBusSamples[internalBusWrite] = output;
- internalBusWrite = (internalBusWrite + 1) % INTERNAL_BUFFER_SIZE;
}
}
@@ -272,31 +272,64 @@ void initEncoder()
{
memset(&encoder,0,sizeof(Encoder));
+
encoder.bufferSize = 0;
- encoder.bitCount = 8;
+
+
+ /*
+ 8-FSK:
+ 3 bits per symbol
+ 8 frequencies
+ */
+ encoder.bitsPerSymbol = 3;
+ encoder.toneCount = 8;
+
+
encoder.byteIndex = 0;
- encoder.startFreq = 1000;
- encoder.endFreq = 5000;
- encoder.dataSpacing = 250;
- encoder.symbolSamples = SAMPLE_RATE / 50;
- encoder.clock0 = goertzel_bin_freq(
- 6000,
- SAMPLE_RATE,
- encoder.symbolSamples
-);
+ encoder.startFreq =
+ DEFAULT_START_FREQ;
+
+ encoder.endFreq =
+ DEFAULT_END_FREQ;
+
+
+ uint16_t freqRange =
+ encoder.endFreq -
+ encoder.startFreq;
+
+
+ /*
+ N tones need N-1 intervals
+ */
+ encoder.dataSpacing =
+ freqRange /
+ (encoder.toneCount-1);
+
+
+ encoder.symbolSamples =
+ SAMPLE_RATE /
+ DEFAULT_SYMBOL_RATE;
+
+
+ encoder.clock0 =
+ goertzel_bin_freq(
+ DEFAULT_CLOCK0_FREQ,
+ SAMPLE_RATE,
+ encoder.symbolSamples);
+
+
+ encoder.clock1 =
+ goertzel_bin_freq(
+ DEFAULT_CLOCK1_FREQ,
+ SAMPLE_RATE,
+ encoder.symbolSamples);
- encoder.clock1 = goertzel_bin_freq(
- 6300,
- SAMPLE_RATE,
- encoder.symbolSamples
- );
encoder.state = TX_IDLE;
encoder.transmitting = false;
-
}
void initAudioEncoder() {
@@ -307,16 +340,35 @@ void initAudioEncoder() {
spec.freq = SAMPLE_RATE;
spec.format = AUDIO_F32SYS;
spec.channels = 1;
- spec.samples = 4096;
+ spec.samples = 960; // requested buffer
spec.callback = audio_callback;
encodedAudioData.encoder = &encoder;
spec.userdata = &encodedAudioData;
- dev = SDL_OpenAudioDevice(NULL, 0, &spec, NULL, 0);
- printf("Encoder device opened: dev=%u\n", dev);
+ SDL_AudioSpec realSpec = {0};
+ dev = SDL_OpenAudioDevice(NULL, 0, &spec, &realSpec, 0);
+ printf("Encoder device opened: dev=%u (requested %dHz/%d samples)\n", dev, spec.freq, spec.samples);
if (dev == 0) {
printf("Failed to open audio encoder: %s\n", SDL_GetError());
SDL_Quit();
}
+
+ // Update encoder sample rate and symbolSamples based on actual device
+ encoder.sampleRate = realSpec.freq;
+ encoder.symbolSamples = realSpec.freq / DEFAULT_SYMBOL_RATE;
+ encoder.clock0 = goertzel_bin_freq(
+ DEFAULT_CLOCK0_FREQ,
+ encoder.sampleRate,
+ encoder.symbolSamples
+ );
+ encoder.clock1 = goertzel_bin_freq(
+ DEFAULT_CLOCK1_FREQ,
+ encoder.sampleRate,
+ encoder.symbolSamples
+ );
+
+ printf("Encoder real spec: freq=%d channels=%d samples=%d -> symbolSamples=%u\n",
+ realSpec.freq, realSpec.channels, realSpec.samples, encoder.symbolSamples);
+
SDL_PauseAudioDevice(dev, 0);
}
diff --git a/encoder.h b/encoder.h
index d21f391..4db95ca 100644
--- a/encoder.h
+++ b/encoder.h
@@ -6,8 +6,8 @@
#define AUDIOCODER_ENCODER_H
#define SAMPLE_RATE 48000
-#define MAX_BITS 16
-#define CLOCK_VOICE MAX_BITS
+#define CLOCK_VOICE 0
+#define DATA_VOICE 1
#define MAX_PACKET 223
#include
@@ -23,21 +23,23 @@ typedef struct SynthVoice {
typedef enum {
TX_IDLE,
TX_PREAMBLE,
- TX_SYNC,
- TX_DATA,
+ TX_PACKET,
TX_END
} TxState;
-
typedef struct {
- unsigned int bitCount;
+ unsigned int bitsPerSymbol;
+ unsigned int toneCount;
unsigned int byteIndex;
unsigned int bitIndex;
uint8_t buffer[1024 * 1024 * 8];
unsigned int bufferSize;
+ uint8_t packet[MAX_PACKET];
+ uint32_t packetSize;
+ uint32_t packetIndex;
uint16_t startFreq;
@@ -53,6 +55,7 @@ typedef struct {
uint32_t symbolSamples;
uint32_t samplesRemaining;
+ uint32_t sampleRate; // actual device sample rate
TxState state;
@@ -66,7 +69,7 @@ typedef struct {
typedef struct AudioData {
- SynthVoice synthVoices[MAX_BITS + 1];
+ SynthVoice synthVoices[2];
Encoder *encoder;
} AudioData;
@@ -74,18 +77,12 @@ extern Encoder encoder;
extern AudioData encodedAudioData;
extern SDL_AudioDeviceID dev;
-// Internal audio bus for loopback - simple ring buffer
-#define INTERNAL_BUFFER_SIZE 262144
-extern float internalBusSamples[INTERNAL_BUFFER_SIZE];
-extern volatile uint32_t internalBusWrite;
-extern volatile uint32_t internalBusRead;
-
void startEncoder(Encoder *enc);
void stopEncoder(Encoder *enc);
void initAudioEncoder();
-extern const uint8_t syncBytes[4];
+extern const uint8_t syncSymbols[8];
#endif //AUDIOCODER_ENCODER_H
diff --git a/goertzel.c b/goertzel.c
index 9e78553..5fc1301 100644
--- a/goertzel.c
+++ b/goertzel.c
@@ -6,70 +6,31 @@
#include
-void goertzel_init(
- Goertzel *g,
- float freq,
- float sampleRate,
- int samples)
+void goertzel_init(Goertzel *g, float freq, float sampleRate, int samples)
{
-
- float omega =
- 2.0f * M_PI * freq / sampleRate;
-
-
- g->coeff =
- 2.0f*cosf(omega);
-
-
+ float omega = 2.0f * M_PI * freq / sampleRate;
+ g->coeff = 2.0f*cosf(omega);
g->s1=0;
g->s2=0;
-
}
-
-
-void goertzel_add(
- Goertzel *g,
- float sample)
+void goertzel_add(Goertzel *g, float sample)
{
-
- float s =
- sample +
- g->coeff*g->s1 -
- g->s2;
-
-
+ float s = sample + g->coeff*g->s1 - g->s2;
g->s2=g->s1;
g->s1=s;
-
}
-
-
-float goertzel_energy(
- Goertzel *g)
+float goertzel_energy(Goertzel *g)
{
-
- float power =
- g->s1*g->s1 +
- g->s2*g->s2 -
- g->coeff*g->s1*g->s2;
-
-
+ float power = g->s1*g->s1 + g->s2*g->s2 - g->coeff*g->s1*g->s2;
g->s1=0;
g->s2=0;
-
-
return power;
-
}
-int goertzel_bin_freq(
- int freq,
- int sampleRate,
- int samples)
+int goertzel_bin_freq(int freq, int sampleRate, int samples)
{
int bin = (freq * samples + sampleRate / 2) / sampleRate;
-
return bin * sampleRate / samples;
}
\ No newline at end of file
diff --git a/main.c b/main.c
index 7396d94..625df02 100644
--- a/main.c
+++ b/main.c
@@ -1,14 +1,11 @@
-#include
#include
#include
#include
#include
#include "font.h"
-#include "cpustatusui.h"
#include "decoder.h"
#include "goertzel.h"
#include "texteditor.h"
-#include "hexdump.h"
//The window we'll be rendering to
SDL_Window *window = NULL;
@@ -38,8 +35,7 @@ static const char *tx_state_name(TxState state) {
switch (state) {
case TX_IDLE: return "IDLE";
case TX_PREAMBLE: return "PREAMBLE";
- case TX_SYNC: return "SYNC";
- case TX_DATA: return "DATA";
+ case TX_PACKET: return "PACKET";
case TX_END: return "END";
}
return "?";
@@ -48,9 +44,9 @@ static const char *tx_state_name(TxState state) {
static const char *rx_state_name(RxState state) {
switch (state) {
case RX_SEARCH: return "SEARCH";
- case RX_ALIGN: return "ALIGN";
case RX_SYNC: return "SYNC";
- case RX_LOCKED: return "LOCKED";
+ case RX_MAGIC: return "MAGIC";
+ case RX_DATA: return "DATA";
}
return "?";
}
@@ -65,8 +61,8 @@ static const char *setting_name(int setting) {
case 5: return "CLOCK1";
case 6: return "OUT DEVICE";
case 7: return "IN DEVICE";
+ default: return "UNKNOWN";
}
- return "?";
}
static void apply_modem_settings(void) {
@@ -79,12 +75,12 @@ static void apply_modem_settings(void) {
encoder.clock0,
SAMPLE_RATE,
encoder.symbolSamples);
+
encoder.clock1 = goertzel_bin_freq(
encoder.clock1,
SAMPLE_RATE,
encoder.symbolSamples);
- decoder.bitCount = encoder.bitCount;
decoder.startFreq = encoder.startFreq;
decoder.endFreq = encoder.endFreq;
decoder.dataSpacing = encoder.dataSpacing;
@@ -135,6 +131,7 @@ static void adjust_modem_setting(int delta) {
static void render_modem_text(SDL_Renderer *renderer) {
char line[256];
+ // TX info on the left
snprintf(
line,
sizeof(line),
@@ -147,6 +144,7 @@ static void render_modem_text(SDL_Renderer *renderer) {
encoder.syncIndex);
renderText(renderer, smallerFont, line, 10, 8);
+ // RX info on the right
snprintf(
line,
sizeof(line),
@@ -156,18 +154,19 @@ static void render_modem_text(SDL_Renderer *renderer) {
decoder.lastDecodedByte,
decoder.bestSyncScore,
decoder.bestSyncPhase);
- renderText(renderer, smallerFont, line, 10, 22);
+ renderText(renderer, smallerFont, line, 640, 8);
+ // Clock info below RX
snprintf(
line,
sizeof(line),
- "CLK E0 %.5f E1 %.5f R %.1f HIST %02X/%u",
+ "CLK E0 %08.5f E1 %08.5f R %02.1f HIST %02X/%u",
decoder.clockEnergy0,
decoder.clockEnergy1,
decoder.clockRatio,
decoder.clockHistory,
decoder.clockCount);
- renderText(renderer, smallerFont, line, 10, 36);
+ renderText(renderer, smallerFont, line, 640, 22);
// Settings display with highlighting
const char *settingStr = setting_name(selectedModemSetting);
@@ -192,10 +191,11 @@ static void render_modem_text(SDL_Renderer *renderer) {
}
static void render_tone_bars(SDL_Renderer *renderer) {
- const int tones = decoder.bitCount * 2 + 2;
+ const int dataTones = MAX_FSK_TONES;
+ const int tones = dataTones + 2; // clocks are indices 0,1
const int x = 500;
- const int y = 560;
- const int maxHeight = 72;
+ const int y = 650;
+ const int maxHeight = 192;
const int barWidth = 8;
const int gap = 4;
@@ -203,10 +203,15 @@ static void render_tone_bars(SDL_Renderer *renderer) {
SDL_Rect frame = {x - 4, y - maxHeight - 4, tones * (barWidth + gap) + 4, maxHeight + 24};
SDL_RenderDrawRect(renderer, &frame);
+ // Draw clocks first (indices 0 and 1)
for (int i = 0; i < tones; i++) {
+ int idx;
+ if (i < 2) idx = i; // clock indices 0,1
+ else idx = 2 + (i - 2); // data indices start at 2
+
float ratio = 0.0f;
if (decoder.maxEnergy > 0.0f)
- ratio = decoder.energies[i] / decoder.maxEnergy;
+ ratio = decoder.energies[idx] / decoder.maxEnergy;
if (ratio > 1.0f)
ratio = 1.0f;
@@ -216,18 +221,18 @@ static void render_tone_bars(SDL_Renderer *renderer) {
barRect.x = x + i * (barWidth + gap);
barRect.y = y - barRect.h;
- if (i >= decoder.bitCount * 2)
- SDL_SetRenderDrawColor(renderer, 255, 192, 64, 255);
- else if (i & 1)
- SDL_SetRenderDrawColor(renderer, 80, 180, 255, 255);
+ if (idx < 2)
+ SDL_SetRenderDrawColor(renderer, 255, 192, 64, 255); // clock
+ else if ((idx - 2) & 1)
+ SDL_SetRenderDrawColor(renderer, 80, 180, 255, 255); // 1-tone
else
- SDL_SetRenderDrawColor(renderer, 80, 255, 140, 255);
+ SDL_SetRenderDrawColor(renderer, 80, 255, 140, 255); // 0-tone
SDL_RenderFillRect(renderer, &barRect);
}
char label[128];
- snprintf(label, sizeof(label), "TONE ENERGY: BIT0..BIT7, CLOCK0, CLOCK1");
+ snprintf(label, sizeof(label), "TONE ENERGY: CLOCK0, CLOCK1, BIT0..BIT15");
renderText(renderer, smallerFont, label, x, y + 6);
}
@@ -320,6 +325,9 @@ int init() {
updateDump(&transmitEditor, renderer);
+ // Auto-start encoder for testing
+ //startEncoder(&encoder);
+
SDL_Rect viewport = {0, 0, SCREEN_WIDTH, SCREEN_HEIGHT};
SDL_RenderSetViewport(renderer, &viewport);
@@ -477,6 +485,7 @@ int processEvent(SDL_Event e) {
break;
case SDLK_F5:
updateDump(&transmitEditor, renderer);
+ updateDump(&receivedEditor, renderer);
break;
case SDLK_F7:
startEncoder(&encoder);
diff --git a/modem_settings.h b/modem_settings.h
new file mode 100644
index 0000000..c509aae
--- /dev/null
+++ b/modem_settings.h
@@ -0,0 +1,15 @@
+#ifndef MODEM_SETTINGS_H
+#define MODEM_SETTINGS_H
+
+// Shared modem configuration defaults
+#define DEFAULT_BIT_COUNT 16
+#define DEFAULT_START_FREQ 1500
+#define DEFAULT_END_FREQ 5000
+#define DEFAULT_DATA_SPACING 250
+// Symbols per second (used to compute samples per symbol)
+#define DEFAULT_SYMBOL_RATE 10
+
+#define DEFAULT_CLOCK0_FREQ 1000
+#define DEFAULT_CLOCK1_FREQ 1300
+
+#endif // MODEM_SETTINGS_H
diff --git a/packet.c b/packet.c
index 42c9f64..9391ff9 100644
--- a/packet.c
+++ b/packet.c
@@ -1,136 +1,126 @@
-//
-// Created by bruno on 13. 7. 2026.
-//
-
#include "packet.h"
-#include "crc32.h"
-#include
-#include
+#include
+
+#include "cpustatusui.h"
+#include "decoder.h"
+#include "protocol.h"
-#define MAGIC 0xCA55
-
-
-#define PACKET_SIZE 223
-
-
-uint8_t *build_stream(
- uint8_t *file,
- uint32_t size,
- uint32_t *outSize) {
- uint32_t packets =
- (size + PACKET_SIZE - 1)
- /
- PACKET_SIZE;
-
-
- /*
- estimate:
-
- preamble 256 bytes
- sync 16 bytes
- packets:
- header 4
- payload 223
- crc 4
- */
-
-
- uint32_t capacity =
- 256 +
- 16 +
- packets * (4 + 2 + 2 + 223 + 4);
-
-
- uint8_t *out =
- malloc(capacity);
-
-
- uint32_t pos = 0;
-
-
- /*
- preamble
- */
-
- for (int i = 0; i < 256; i++)
- out[pos++] = 0x55;
-
-
- /*
- sync
- */
-
- uint8_t sync[] =
- {
- 0xD3,
- 0x91,
- 0xAC,
- 0x55,
- 0x77,
- 0x22,
- 0xF0,
- 0x0F
- };
+void encoder_build_packet(
+ Encoder *enc,
+ uint8_t *data,
+ uint8_t length)
+{
+ uint32_t p=0;
memcpy(
- out + pos,
- sync,
- sizeof(sync));
+ &enc->packet[p],
+ PACKET_MAGIC,
+ MAGIC_SIZE);
- pos += sizeof(sync);
+ p+=MAGIC_SIZE;
- uint32_t offset = 0;
+ enc->packet[p++]=length;
- for (uint32_t p = 0; p < packets; p++) {
- uint16_t magic = MAGIC;
+ memcpy(
+ &enc->packet[p],
+ data,
+ length);
- uint16_t packet = p;
+ p+=length;
- uint16_t len =
- size - offset > PACKET_SIZE ? PACKET_SIZE : size - offset;
+ uint16_t crc =
+ crc16(
+ &enc->packet[0],
+ p);
- memcpy(out + pos, &magic, 2);
- pos += 2;
+ enc->packet[p++]=crc&0xff;
+ enc->packet[p++]=crc>>8;
- memcpy(out + pos, &packet, 2);
- pos += 2;
+ enc->packetSize=p;
+}
+
+bool decoder_parse_packet(Decoder *dec)
+{
+ if(dec->bufferSize < MAGIC_SIZE + 1 + 2)
+ return false;
+
+ if(memcmp(dec->buffer,
+ PACKET_MAGIC,
+ MAGIC_SIZE))
+ {
+ printf("Bad magic\n");
+ return false;
+ }
+
+ uint8_t length =
+ dec->buffer[MAGIC_SIZE];
+
+ uint32_t packetSize =
+ MAGIC_SIZE +
+ 1 +
+ length +
+ 2;
+
+ if(dec->bufferSize < packetSize)
+ return false;
+
+ uint16_t received =
+ dec->buffer[packetSize-2] |
+ (dec->buffer[packetSize-1] << 8);
+
+ uint16_t calculated =
+ crc16(dec->buffer,
+ packetSize-2);
+
+ if(received != calculated)
+ {
+ printf("CRC FAIL\n");
+ return true;
+ }
+
+ printf("PACKET OK len=%d\n", length);
+
+ printf("Payload:\n");
+
+ for(int i=0;ibuffer[MAGIC_SIZE+1+i]);
+
+ printf("\n");
+
+ return true;
+}
+
+uint16_t crc16(
+ const uint8_t *data,
+ uint32_t len)
+{
+ uint16_t crc = 0xFFFF;
- memcpy(out + pos, &len, 2);
- pos += 2;
+ for(uint32_t i=0;i> 1) ^ 0xA001;
+ else
+ crc >>= 1;
+ }
}
- *outSize = pos;
-
- return out;
-}
+ return crc;
+}
\ No newline at end of file
diff --git a/packet.h b/packet.h
index f3eb5c1..3232d5d 100644
--- a/packet.h
+++ b/packet.h
@@ -1,17 +1,25 @@
-//
-// Created by bruno on 13. 7. 2026.
-//
-
-#ifndef AUDIOCODER_PACKET_H
-#define AUDIOCODER_PACKET_H
+#ifndef PACKET_H
+#define PACKET_H
#include
+#include "decoder.h"
+#include "encoder.h"
-uint8_t *build_stream(
- uint8_t *file,
- uint32_t size,
- uint32_t *outSize
-);
+#define PREAMBLE_SYMBOLS 128
+#define PREAMBLE_CHECK_SYMBOLS 16
-#endif //AUDIOCODER_PACKET_H
+#define MAGIC0 0xA5
+#define MAGIC1 0x5A
+#define MAGIC2 0xC3
+#define MAGIC3 0x3C
+
+#define MAX_PACKET 1024
+
+uint16_t crc16(const uint8_t *data, uint32_t len);
+
+void encoder_build_packet(Encoder *enc, uint8_t *data, uint8_t length);
+
+bool decoder_parse_packet(Decoder *dec);
+
+#endif
\ No newline at end of file
diff --git a/protocol.h b/protocol.h
new file mode 100644
index 0000000..9e9b211
--- /dev/null
+++ b/protocol.h
@@ -0,0 +1,23 @@
+#ifndef PROTOCOL_H
+#define PROTOCOL_H
+
+#include
+
+#define MAGIC_SIZE 4
+#define CRC_SIZE 2
+
+static const uint8_t PACKET_MAGIC[MAGIC_SIZE] =
+{
+ 0xA5,
+ 0x5A,
+ 0xC3,
+ 0x3C
+};
+
+
+uint16_t crc16(
+ const uint8_t *data,
+ uint32_t len);
+
+
+#endif
\ No newline at end of file