thing
This commit is contained in:
@@ -9,72 +9,93 @@
|
||||
#include <SDL_stdinc.h>
|
||||
|
||||
#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;
|
||||
i<enc->bitsPerSymbol;
|
||||
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<<i;
|
||||
|
||||
|
||||
enc->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;i<enc->bitCount;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;i<enc->bitCount;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;i<enc->bitCount;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;i<MAX_BITS+1;i++)
|
||||
for(int i=0;i<2;i++)
|
||||
{
|
||||
encodedAudioData.synthVoices[i].volume=0;
|
||||
}
|
||||
@@ -234,15 +237,16 @@ void audio_callback(void *userdata, Uint8 *stream, int len) {
|
||||
int samples = len / sizeof(float);
|
||||
|
||||
for (int i = 0; i < samples; i++) {
|
||||
if (enc->samplesRemaining == 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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user