mirror of
https://github.com/radio95-rnt/rds95.git
synced 2026-02-27 04:43:52 +01:00
add rt timeout, clean up code, add error checking for rdsencoder save, add rds eon af and remove stale values
This commit is contained in:
2
.vscode/.server-controller-port.log
vendored
2
.vscode/.server-controller-port.log
vendored
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"port": 13452,
|
"port": 13452,
|
||||||
"time": 1743622887495,
|
"time": 1743850931238,
|
||||||
"version": "0.0.3"
|
"version": "0.0.3"
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
cmake_minimum_required(VERSION 3.10)
|
cmake_minimum_required(VERSION 3.10)
|
||||||
|
|
||||||
project(rds95 VERSION 1.2)
|
project(rds95 VERSION 1.3)
|
||||||
|
|
||||||
add_compile_options(-Wall -Werror -Wextra -pedantic -O2 -std=c18 -march=native -DVERSION=\"${PROJECT_VERSION}\")
|
add_compile_options(-Wall -Werror -Wextra -pedantic -O2 -std=c18 -march=native -DVERSION=\"${PROJECT_VERSION}\")
|
||||||
|
|
||||||
|
|||||||
137
gen_wave.py
137
gen_wave.py
@@ -4,7 +4,7 @@ FFT = PLOT and True
|
|||||||
import math
|
import math
|
||||||
import io, os
|
import io, os
|
||||||
if PLOT: import matplotlib.pyplot as plt
|
if PLOT: import matplotlib.pyplot as plt
|
||||||
if FFT: import numpy as np # Import numpy for FFT
|
if FFT: import numpy as np
|
||||||
|
|
||||||
DATA_RATE = 1187.5
|
DATA_RATE = 1187.5
|
||||||
SIZE_RATIO = 2
|
SIZE_RATIO = 2
|
||||||
@@ -16,33 +16,33 @@ if not sample_rate.is_integer(): raise ValueError("Need a even value")
|
|||||||
|
|
||||||
# this is modified from ChristopheJacquet's pydemod
|
# this is modified from ChristopheJacquet's pydemod
|
||||||
def rrcosfilter(NumSamples):
|
def rrcosfilter(NumSamples):
|
||||||
T_delta = 1/float(sample_rate)
|
T_delta = 1/float(sample_rate)
|
||||||
sample_num = list(range(NumSamples))
|
sample_num = list(range(NumSamples))
|
||||||
h_rrc = [0.0] * NumSamples
|
h_rrc = [0.0] * NumSamples
|
||||||
SymbolPeriod = 1/(2*DATA_RATE)
|
SymbolPeriod = 1/(2*DATA_RATE)
|
||||||
|
|
||||||
for x in sample_num:
|
for x in sample_num:
|
||||||
t = (x-NumSamples/2)*T_delta
|
t = (x-NumSamples/2)*T_delta
|
||||||
if t == 0.0:
|
if t == 0.0:
|
||||||
h_rrc[x] = 1.0 - 1 + (4/math.pi)
|
h_rrc[x] = 1.0 - 1 + (4/math.pi)
|
||||||
elif t == SymbolPeriod/4:
|
elif t == SymbolPeriod/4:
|
||||||
h_rrc[x] = (1/math.sqrt(2))*(((1+2/math.pi)* \
|
h_rrc[x] = (1/math.sqrt(2))*(((1+2/math.pi)* \
|
||||||
(math.sin(math.pi/4))) + ((1-2/math.pi)*(math.cos(math.pi/4))))
|
(math.sin(math.pi/4))) + ((1-2/math.pi)*(math.cos(math.pi/4))))
|
||||||
elif t == -SymbolPeriod/4:
|
elif t == -SymbolPeriod/4:
|
||||||
h_rrc[x] = (1/math.sqrt(2))*(((1+2/math.pi)* \
|
h_rrc[x] = (1/math.sqrt(2))*(((1+2/math.pi)* \
|
||||||
(math.sin(math.pi/4))) + ((1-2/math.pi)*(math.cos(math.pi/4))))
|
(math.sin(math.pi/4))) + ((1-2/math.pi)*(math.cos(math.pi/4))))
|
||||||
else:
|
else:
|
||||||
h_rrc[x] = (4*(t/SymbolPeriod)*math.cos(math.pi*t*2/SymbolPeriod))/ \
|
h_rrc[x] = (4*(t/SymbolPeriod)*math.cos(math.pi*t*2/SymbolPeriod))/ \
|
||||||
(math.pi*t*(1-(4*t/SymbolPeriod)*(4*t/SymbolPeriod))/SymbolPeriod)
|
(math.pi*t*(1-(4*t/SymbolPeriod)*(4*t/SymbolPeriod))/SymbolPeriod)
|
||||||
|
|
||||||
return h_rrc
|
return h_rrc
|
||||||
|
|
||||||
def convolve(a, b):
|
def convolve(a, b):
|
||||||
out = [0] * (len(a) + len(b) - 1)
|
out = [0] * (len(a) + len(b) - 1)
|
||||||
for i in range(len(a)):
|
for i in range(len(a)):
|
||||||
for j in range(len(b)):
|
for j in range(len(b)):
|
||||||
out[i+j] += a[i] * b[j]
|
out[i+j] += a[i] * b[j]
|
||||||
return out
|
return out
|
||||||
|
|
||||||
PATH = os.path.dirname(os.path.abspath(__file__))
|
PATH = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
@@ -63,61 +63,58 @@ outc.write(header)
|
|||||||
outh.write(header)
|
outh.write(header)
|
||||||
|
|
||||||
def generate():
|
def generate():
|
||||||
l = ratio // 2
|
l = ratio // 2
|
||||||
|
|
||||||
sample = [0.0] * (16*l)
|
sample = [0.0] * (16*l)
|
||||||
sample[l] = 1
|
sample[l] = 1
|
||||||
sample[2*l] = -1
|
sample[2*l] = -1
|
||||||
|
|
||||||
sf = rrcosfilter(l*16)
|
sf = rrcosfilter(l*16)
|
||||||
shapedSamples = convolve(sample, sf)
|
shapedSamples = convolve(sample, sf)
|
||||||
|
|
||||||
lowest = 0
|
lowest = 0
|
||||||
lowest_idx = 0
|
lowest_idx = 0
|
||||||
highest = 0
|
highest = 0
|
||||||
highest_idx = 0
|
highest_idx = 0
|
||||||
for i,j in enumerate(shapedSamples):
|
for i,j in enumerate(shapedSamples):
|
||||||
if j < lowest:
|
if j < lowest:
|
||||||
lowest = j
|
lowest = j
|
||||||
lowest_idx = i
|
lowest_idx = i
|
||||||
if j > highest:
|
if j > highest:
|
||||||
highest = j
|
highest = j
|
||||||
highest_idx = i
|
highest_idx = i
|
||||||
middle = int((lowest_idx+highest_idx)/2)
|
middle = int((lowest_idx+highest_idx)/2)
|
||||||
|
|
||||||
out = shapedSamples[middle-int(ratio*SIZE_RATIO):middle+int(ratio*SIZE_RATIO)]
|
out = shapedSamples[middle-int(ratio*SIZE_RATIO):middle+int(ratio*SIZE_RATIO)]
|
||||||
out = [2 * (i - min(out)) / (max(out) - min(out)) - 1 for i in out]
|
out = [2 * (i - min(out)) / (max(out) - min(out)) - 1 for i in out]
|
||||||
if max(out) > 1 or min(out) < -1: raise Exception("Clipped")
|
if max(out) > 1 or min(out) < -1: raise Exception("Clipped")
|
||||||
print(f"{len(out)=} {len(out)/sample_rate=} {(len(out)/sample_rate)/(1/DATA_RATE)=} {1/DATA_RATE=}")
|
print(f"{len(out)=} {len(out)/sample_rate=} {(len(out)/sample_rate)/(1/DATA_RATE)=} {1/DATA_RATE=}")
|
||||||
|
|
||||||
if PLOT:
|
if PLOT:
|
||||||
# Plot the waveform
|
plt.plot(out, label="out")
|
||||||
plt.plot(out, label="out")
|
plt.legend()
|
||||||
plt.legend()
|
plt.grid(True)
|
||||||
plt.grid(True)
|
plt.show()
|
||||||
plt.show()
|
|
||||||
|
|
||||||
if FFT:
|
if FFT:
|
||||||
# Compute the FFT of the waveform
|
N = len(out)
|
||||||
N = len(out)
|
fft_out = np.fft.fft(out)
|
||||||
fft_out = np.fft.fft(out)
|
fft_freqs = np.fft.fftfreq(N, d=1/sample_rate)
|
||||||
fft_freqs = np.fft.fftfreq(N, d=1/sample_rate)
|
|
||||||
|
|
||||||
# Plot the magnitude of the FFT
|
plt.figure(figsize=(10, 6))
|
||||||
plt.figure(figsize=(10, 6))
|
plt.plot(fft_freqs[:N//2], np.abs(fft_out)[:N//2])
|
||||||
plt.plot(fft_freqs[:N//2], np.abs(fft_out)[:N//2]) # Plot only the positive frequencies
|
plt.xlim(0,DATA_RATE*3)
|
||||||
plt.xlim(0,DATA_RATE*3)
|
plt.title("FFT of the waveform")
|
||||||
plt.title("FFT of the waveform")
|
plt.xlabel("Frequency (Hz)")
|
||||||
plt.xlabel("Frequency (Hz)")
|
plt.ylabel("Magnitude")
|
||||||
plt.ylabel("Magnitude")
|
plt.grid(True)
|
||||||
plt.grid(True)
|
plt.show()
|
||||||
plt.show()
|
|
||||||
|
|
||||||
outc.write(u"float waveform_biphase[{size}] = {{{values}}};\n\n".format(
|
outc.write(u"float waveform_biphase[{size}] = {{{values}}};\n\n".format(
|
||||||
values = u", ".join(map(str, out)),
|
values = u", ".join(map(str, out)),
|
||||||
size = len(out)))
|
size = len(out)))
|
||||||
|
|
||||||
outh.write(u"extern float waveform_biphase[{size}];\n".format(size=len(out)))
|
outh.write(u"extern float waveform_biphase[{size}];\n".format(size=len(out)))
|
||||||
|
|
||||||
generate()
|
generate()
|
||||||
|
|
||||||
|
|||||||
@@ -236,6 +236,12 @@ static void handle_grpseq2(char *arg, RDSModulator* mod, char* output) {
|
|||||||
strcpy(output, "+\0");
|
strcpy(output, "+\0");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void handle_dttmout(char *arg, RDSModulator* mod, char* output) {
|
||||||
|
mod->enc->data[mod->enc->program].original_rt_text_timeout = atoi(arg);
|
||||||
|
mod->enc->data[mod->enc->program].rt_text_timeout = mod->enc->data[mod->enc->program].original_rt_text_timeout;
|
||||||
|
strcpy(output, "+\0");
|
||||||
|
}
|
||||||
|
|
||||||
static void handle_level(char *arg, RDSModulator* mod, char* output) {
|
static void handle_level(char *arg, RDSModulator* mod, char* output) {
|
||||||
mod->params.level = atoi(arg)/255.0f;
|
mod->params.level = atoi(arg)/255.0f;
|
||||||
strcpy(output, "+\0");
|
strcpy(output, "+\0");
|
||||||
@@ -313,7 +319,7 @@ static void handle_init(char *arg, RDSModulator* mod, char* output) {
|
|||||||
static void handle_ver(char *arg, RDSModulator* mod, char* output) {
|
static void handle_ver(char *arg, RDSModulator* mod, char* output) {
|
||||||
(void)arg;
|
(void)arg;
|
||||||
(void)mod;
|
(void)mod;
|
||||||
sprintf(output, "Firmware v. 1.2 - (C) 2025 radio95");
|
sprintf(output, "Encoder v. %s - (C) 2025 radio95", VERSION);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_eonen(char *arg, char *pattern, RDSModulator* mod, char* output) {
|
static void handle_eonen(char *arg, char *pattern, RDSModulator* mod, char* output) {
|
||||||
@@ -453,6 +459,7 @@ static const command_handler_t commands_eq8[] = {
|
|||||||
{"PROGRAM", handle_program, 7},
|
{"PROGRAM", handle_program, 7},
|
||||||
{"RDS2MOD", handle_rds2mod, 7},
|
{"RDS2MOD", handle_rds2mod, 7},
|
||||||
{"GRPSEQ2", handle_grpseq2, 7},
|
{"GRPSEQ2", handle_grpseq2, 7},
|
||||||
|
{"DTTMOUT", handle_dttmout, 7},
|
||||||
};
|
};
|
||||||
static const command_handler_t commands_exact[] = {
|
static const command_handler_t commands_exact[] = {
|
||||||
{"INIT", handle_init, 4},
|
{"INIT", handle_init, 4},
|
||||||
|
|||||||
@@ -11,3 +11,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define M_2PI (M_PI * 2.0)
|
#define M_2PI (M_PI * 2.0)
|
||||||
|
|
||||||
|
#ifndef VERSION
|
||||||
|
#define VERSION "-.-"
|
||||||
|
#endif
|
||||||
@@ -17,7 +17,7 @@ int _strnlen(const char *s, int maxlen) {
|
|||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
// For RDS2 RFT, and UECP
|
// For RDS2 RFT, file error checking, and UECP
|
||||||
uint16_t crc16_ccitt(char* data, uint16_t len) {
|
uint16_t crc16_ccitt(char* data, uint16_t len) {
|
||||||
uint16_t i, crc=0xFFFF;
|
uint16_t i, crc=0xFFFF;
|
||||||
for (i=0; i < len; i++ ) {
|
for (i=0; i < len; i++ ) {
|
||||||
|
|||||||
@@ -83,9 +83,7 @@ void init_rds_modulator(RDSModulator* rdsMod, RDSEncoder* enc) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
float get_rds_sample(RDSModulator* rdsMod, uint8_t stream) {
|
float get_rds_sample(RDSModulator* rdsMod, uint8_t stream) {
|
||||||
uint16_t idx;
|
|
||||||
float *cur_waveform;
|
float *cur_waveform;
|
||||||
float sample;
|
|
||||||
if (rdsMod->data[stream].sample_count == SAMPLES_PER_BIT) {
|
if (rdsMod->data[stream].sample_count == SAMPLES_PER_BIT) {
|
||||||
if (rdsMod->data[stream].bit_pos == BITS_PER_GROUP) {
|
if (rdsMod->data[stream].bit_pos == BITS_PER_GROUP) {
|
||||||
get_rds_bits(rdsMod->enc, rdsMod->data[stream].bit_buffer, stream);
|
get_rds_bits(rdsMod->enc, rdsMod->data[stream].bit_buffer, stream);
|
||||||
@@ -96,7 +94,7 @@ float get_rds_sample(RDSModulator* rdsMod, uint8_t stream) {
|
|||||||
rdsMod->data[stream].prev_output = rdsMod->data[stream].cur_output;
|
rdsMod->data[stream].prev_output = rdsMod->data[stream].cur_output;
|
||||||
rdsMod->data[stream].cur_output = rdsMod->data[stream].prev_output ^ rdsMod->data[stream].cur_bit;
|
rdsMod->data[stream].cur_output = rdsMod->data[stream].prev_output ^ rdsMod->data[stream].cur_bit;
|
||||||
|
|
||||||
idx = rdsMod->data[stream].in_sample_index;
|
uint16_t idx = rdsMod->data[stream].in_sample_index;
|
||||||
cur_waveform = waveform[rdsMod->data[stream].cur_output];
|
cur_waveform = waveform[rdsMod->data[stream].cur_output];
|
||||||
|
|
||||||
for (uint16_t i = 0; i < FILTER_SIZE; i++) {
|
for (uint16_t i = 0; i < FILTER_SIZE; i++) {
|
||||||
@@ -111,19 +109,18 @@ float get_rds_sample(RDSModulator* rdsMod, uint8_t stream) {
|
|||||||
}
|
}
|
||||||
rdsMod->data[stream].sample_count++;
|
rdsMod->data[stream].sample_count++;
|
||||||
|
|
||||||
if(rdsMod->data[stream].symbol_shifting.symbol_shift != 0) {
|
float sample = rdsMod->data[stream].sample_buffer[rdsMod->data[stream].out_sample_index];
|
||||||
|
if(stream != 0 && rdsMod->data[stream].symbol_shifting.symbol_shift != 0) {
|
||||||
rdsMod->data[stream].symbol_shifting.sample_buffer[rdsMod->data[stream].symbol_shifting.sample_buffer_idx++] = rdsMod->data[stream].sample_buffer[rdsMod->data[stream].out_sample_index];
|
rdsMod->data[stream].symbol_shifting.sample_buffer[rdsMod->data[stream].symbol_shifting.sample_buffer_idx++] = rdsMod->data[stream].sample_buffer[rdsMod->data[stream].out_sample_index];
|
||||||
|
|
||||||
if (rdsMod->data[stream].symbol_shifting.sample_buffer_idx == rdsMod->data[stream].symbol_shifting.symbol_shift) rdsMod->data[stream].symbol_shifting.sample_buffer_idx = 0;
|
if (rdsMod->data[stream].symbol_shifting.sample_buffer_idx == rdsMod->data[stream].symbol_shifting.symbol_shift) rdsMod->data[stream].symbol_shifting.sample_buffer_idx = 0;
|
||||||
|
|
||||||
sample = rdsMod->data[stream].symbol_shifting.sample_buffer[rdsMod->data[stream].symbol_shifting.sample_buffer_idx];
|
sample = rdsMod->data[stream].symbol_shifting.sample_buffer[rdsMod->data[stream].symbol_shifting.sample_buffer_idx];
|
||||||
} else {
|
|
||||||
sample = rdsMod->data[stream].sample_buffer[rdsMod->data[stream].out_sample_index];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rdsMod->data[stream].sample_buffer[rdsMod->data[stream].out_sample_index++] = 0;
|
rdsMod->data[stream].sample_buffer[rdsMod->data[stream].out_sample_index++] = 0;
|
||||||
if (rdsMod->data[stream].out_sample_index == SAMPLE_BUFFER_SIZE)
|
if (rdsMod->data[stream].out_sample_index == SAMPLE_BUFFER_SIZE) rdsMod->data[stream].out_sample_index = 0;
|
||||||
rdsMod->data[stream].out_sample_index = 0;
|
|
||||||
uint8_t tooutput = 1;
|
uint8_t tooutput = 1;
|
||||||
if (rdsMod->params.rdsgen == 0) {
|
if (rdsMod->params.rdsgen == 0) {
|
||||||
tooutput = 0;
|
tooutput = 0;
|
||||||
|
|||||||
71
src/rds.c
71
src/rds.c
@@ -40,7 +40,7 @@ void saveToFile(RDSEncoder *emp, const char *option) {
|
|||||||
} else if (strcmp(option, "PTYN") == 0) {
|
} else if (strcmp(option, "PTYN") == 0) {
|
||||||
memcpy(tempEncoder.data[emp->program].ptyn, emp->data[emp->program].ptyn, PTYN_LENGTH);
|
memcpy(tempEncoder.data[emp->program].ptyn, emp->data[emp->program].ptyn, PTYN_LENGTH);
|
||||||
tempEncoder.data[emp->program].ptyn_enabled = emp->data[emp->program].ptyn_enabled;
|
tempEncoder.data[emp->program].ptyn_enabled = emp->data[emp->program].ptyn_enabled;
|
||||||
} else if (strcmp(option, "AF") == 0 || strcmp(option, "AFCH") == 0) {
|
} else if (strcmp(option, "AF") == 0) {
|
||||||
memcpy(&(tempEncoder.data[emp->program].af), &(emp->data[emp->program].af), sizeof(emp->data[emp->program].af));
|
memcpy(&(tempEncoder.data[emp->program].af), &(emp->data[emp->program].af), sizeof(emp->data[emp->program].af));
|
||||||
} else if (strcmp(option, "ECC") == 0) {
|
} else if (strcmp(option, "ECC") == 0) {
|
||||||
tempEncoder.data[emp->program].ecc = emp->data[emp->program].ecc;
|
tempEncoder.data[emp->program].ecc = emp->data[emp->program].ecc;
|
||||||
@@ -97,6 +97,7 @@ void saveToFile(RDSEncoder *emp, const char *option) {
|
|||||||
memcpy(&(rdsEncoderfile.rtpData[emp->program]), &(tempEncoder.rtpData[emp->program]), sizeof(RDSRTPlusData));
|
memcpy(&(rdsEncoderfile.rtpData[emp->program]), &(tempEncoder.rtpData[emp->program]), sizeof(RDSRTPlusData));
|
||||||
memcpy(&(rdsEncoderfile.encoder_data), &(tempEncoder.encoder_data), sizeof(RDSEncoderData));
|
memcpy(&(rdsEncoderfile.encoder_data), &(tempEncoder.encoder_data), sizeof(RDSEncoderData));
|
||||||
rdsEncoderfile.program = tempEncoder.program;
|
rdsEncoderfile.program = tempEncoder.program;
|
||||||
|
rdsEncoderfile.crc = crc16_ccitt((char*)&rdsEncoderfile, sizeof(RDSEncoderFile) - sizeof(uint16_t));
|
||||||
|
|
||||||
file = fopen(encoderPath, "wb");
|
file = fopen(encoderPath, "wb");
|
||||||
if (file == NULL) {
|
if (file == NULL) {
|
||||||
@@ -121,10 +122,17 @@ void loadFromFile(RDSEncoder *enc) {
|
|||||||
fclose(file);
|
fclose(file);
|
||||||
|
|
||||||
if (rdsEncoderfile.file_starter != 225 || rdsEncoderfile.file_ender != 95 || rdsEncoderfile.file_middle != 160) {
|
if (rdsEncoderfile.file_starter != 225 || rdsEncoderfile.file_ender != 95 || rdsEncoderfile.file_middle != 160) {
|
||||||
fprintf(stderr, "Invalid file format\n");
|
fprintf(stderr, "[RDSENCODER-FILE] Invalid file format\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint16_t calculated_crc = crc16_ccitt((char*)&rdsEncoderfile, sizeof(RDSEncoderFile) - sizeof(uint16_t));
|
||||||
|
|
||||||
|
if (calculated_crc != rdsEncoderfile.crc) {
|
||||||
|
fprintf(stderr, "[RDSENCODER-FILE] CRC mismatch! Data may be corrupted\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < PROGRAMS; i++) {
|
for (int i = 0; i < PROGRAMS; i++) {
|
||||||
memcpy(&(enc->data[i]), &(rdsEncoderfile.data[i]), sizeof(RDSData));
|
memcpy(&(enc->data[i]), &(rdsEncoderfile.data[i]), sizeof(RDSData));
|
||||||
memcpy(&(enc->rtpData[i]), &(rdsEncoderfile.rtpData[i]), sizeof(RDSRTPlusData));
|
memcpy(&(enc->rtpData[i]), &(rdsEncoderfile.rtpData[i]), sizeof(RDSRTPlusData));
|
||||||
@@ -145,25 +153,48 @@ int rdssaved() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static uint16_t get_next_af(RDSEncoder* enc) {
|
static uint16_t get_next_af(RDSEncoder* enc) {
|
||||||
static uint8_t af_state;
|
|
||||||
uint16_t out;
|
uint16_t out;
|
||||||
|
|
||||||
if (enc->data[enc->program].af.num_afs) {
|
if (enc->data[enc->program].af.num_afs) {
|
||||||
if (af_state == 0) {
|
if (enc->state[enc->program].af_state == 0) {
|
||||||
out = (AF_CODE_NUM_AFS_BASE + enc->data[enc->program].af.num_afs) << 8;
|
out = (AF_CODE_NUM_AFS_BASE + enc->data[enc->program].af.num_afs) << 8;
|
||||||
out |= enc->data[enc->program].af.afs[0];
|
out |= enc->data[enc->program].af.afs[0];
|
||||||
af_state += 1;
|
enc->state[enc->program].af_state += 1;
|
||||||
} else {
|
} else {
|
||||||
out = enc->data[enc->program].af.afs[af_state] << 8;
|
out = enc->data[enc->program].af.afs[enc->state[enc->program].af_state] << 8;
|
||||||
if (enc->data[enc->program].af.afs[af_state + 1])
|
if (enc->data[enc->program].af.afs[enc->state[enc->program].af_state + 1])
|
||||||
out |= enc->data[enc->program].af.afs[af_state + 1];
|
out |= enc->data[enc->program].af.afs[enc->state[enc->program].af_state + 1];
|
||||||
else
|
else
|
||||||
out |= AF_CODE_FILLER;
|
out |= AF_CODE_FILLER;
|
||||||
af_state += 2;
|
enc->state[enc->program].af_state += 2;
|
||||||
}
|
}
|
||||||
if (af_state >= enc->data[enc->program].af.num_entries) af_state = 0;
|
if (enc->state[enc->program].af_state >= enc->data[enc->program].af.num_entries) enc->state[enc->program].af_state = 0;
|
||||||
} else {
|
} else {
|
||||||
out = AF_CODE_NO_AF << 8 | AF_CODE_FILLER;
|
out = AF_CODE_NUM_AFS_BASE << 8 | AF_CODE_FILLER;
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint16_t get_next_af_eon(RDSEncoder* enc, uint8_t eon_index) {
|
||||||
|
uint16_t out;
|
||||||
|
|
||||||
|
if (enc->data[enc->program].eon[eon_index].af.num_afs) {
|
||||||
|
if (enc->state[enc->program].eon_states[eon_index].af_state == 0) {
|
||||||
|
out = (AF_CODE_NUM_AFS_BASE + enc->data[enc->program].af.num_afs) << 8;
|
||||||
|
out |= enc->data[enc->program].eon[eon_index].af.afs[0];
|
||||||
|
enc->state[enc->program].eon_states[eon_index].af_state += 1;
|
||||||
|
} else {
|
||||||
|
out = enc->data[enc->program].eon[eon_index].af.afs[enc->state[enc->program].eon_states[eon_index].af_state] << 8;
|
||||||
|
if (enc->data[enc->program].eon[eon_index].af.afs[enc->state[enc->program].eon_states[eon_index].af_state + 1])
|
||||||
|
out |= enc->data[enc->program].eon[eon_index].af.afs[enc->state[enc->program].eon_states[eon_index].af_state + 1];
|
||||||
|
else
|
||||||
|
out |= AF_CODE_FILLER;
|
||||||
|
enc->state[enc->program].eon_states[eon_index].af_state += 2;
|
||||||
|
}
|
||||||
|
if (enc->state[enc->program].eon_states[eon_index].af_state >= enc->data[enc->program].eon[eon_index].af.num_entries) enc->state[enc->program].eon_states[eon_index].af_state = 0;
|
||||||
|
} else {
|
||||||
|
out = AF_CODE_NUM_AFS_BASE << 8 | AF_CODE_FILLER;
|
||||||
}
|
}
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
@@ -355,7 +386,10 @@ get_eon:
|
|||||||
blocks[2] |= eon.ps[enc->state[enc->program].eon_state*2 + 1];
|
blocks[2] |= eon.ps[enc->state[enc->program].eon_state*2 + 1];
|
||||||
blocks[1] |= enc->state[enc->program].eon_state;
|
blocks[1] |= enc->state[enc->program].eon_state;
|
||||||
break;
|
break;
|
||||||
case 4: // 13
|
case 4:
|
||||||
|
blocks[2] = get_next_af_eon(enc, enc->state[enc->program].eon_index);
|
||||||
|
break;
|
||||||
|
case 5: // 13
|
||||||
if(eon.pty == 0 && eon.tp == 0) {
|
if(eon.pty == 0 && eon.tp == 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -363,12 +397,11 @@ get_eon:
|
|||||||
if(eon.tp) blocks[2] |= eon.ta;
|
if(eon.tp) blocks[2] |= eon.ta;
|
||||||
blocks[1] |= 13;
|
blocks[1] |= 13;
|
||||||
break;
|
break;
|
||||||
// TODO: Add AF
|
|
||||||
}
|
}
|
||||||
|
|
||||||
blocks[3] = eon.pi;
|
blocks[3] = eon.pi;
|
||||||
|
|
||||||
if(enc->state[enc->program].eon_state == 4) {
|
if(enc->state[enc->program].eon_state == 5) {
|
||||||
enc->state[enc->program].eon_index++;
|
enc->state[enc->program].eon_index++;
|
||||||
|
|
||||||
uint8_t i = 0;
|
uint8_t i = 0;
|
||||||
@@ -556,6 +589,14 @@ static void get_rds_group(RDSEncoder* enc, uint16_t *blocks, uint8_t stream) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(enc->data[enc->program].rt1_enabled && enc->data[enc->program].rt_text_timeout != 0) {
|
||||||
|
enc->data[enc->program].rt_text_timeout--;
|
||||||
|
if(enc->data[enc->program].rt_text_timeout == 0) {
|
||||||
|
enc->state[enc->program].rt_update = 1;
|
||||||
|
memccpy(enc->state[enc->program].rt_text, enc->data[enc->program].default_rt, 0, RT_LENGTH);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(enc->data[enc->program].ct && stream == 0) {
|
if(enc->data[enc->program].ct && stream == 0) {
|
||||||
get_rds_ct_group(enc, blocks);
|
get_rds_ct_group(enc, blocks);
|
||||||
goto group_coded;
|
goto group_coded;
|
||||||
@@ -742,6 +783,8 @@ void init_rds_encoder(RDSEncoder* enc) {
|
|||||||
void set_rds_rt1(RDSEncoder* enc, char *rt1) {
|
void set_rds_rt1(RDSEncoder* enc, char *rt1) {
|
||||||
uint8_t i = 0, len = 0;
|
uint8_t i = 0, len = 0;
|
||||||
|
|
||||||
|
enc->data[enc->program].rt_text_timeout = enc->data[enc->program].original_rt_text_timeout;
|
||||||
|
|
||||||
enc->state[enc->program].rt_update = 1;
|
enc->state[enc->program].rt_update = 1;
|
||||||
|
|
||||||
memset(enc->data[enc->program].rt1, ' ', RT_LENGTH);
|
memset(enc->data[enc->program].rt1, ' ', RT_LENGTH);
|
||||||
|
|||||||
13
src/rds.h
13
src/rds.h
@@ -26,8 +26,7 @@
|
|||||||
#define MAX_AFS 25
|
#define MAX_AFS 25
|
||||||
|
|
||||||
#define AF_CODE_FILLER 205
|
#define AF_CODE_FILLER 205
|
||||||
#define AF_CODE_NO_AF 224
|
#define AF_CODE_NUM_AFS_BASE 224
|
||||||
#define AF_CODE_NUM_AFS_BASE AF_CODE_NO_AF
|
|
||||||
#define AF_CODE_LFMF_FOLLOWS 250
|
#define AF_CODE_LFMF_FOLLOWS 250
|
||||||
|
|
||||||
#define PROGRAMS 2
|
#define PROGRAMS 2
|
||||||
@@ -56,9 +55,6 @@ typedef struct {
|
|||||||
char ps[PS_LENGTH];
|
char ps[PS_LENGTH];
|
||||||
char rt1[RT_LENGTH];
|
char rt1[RT_LENGTH];
|
||||||
|
|
||||||
uint8_t dsn;
|
|
||||||
uint8_t psn;
|
|
||||||
|
|
||||||
uint8_t ecc;
|
uint8_t ecc;
|
||||||
|
|
||||||
uint8_t ta : 1;
|
uint8_t ta : 1;
|
||||||
@@ -103,6 +99,9 @@ typedef struct {
|
|||||||
|
|
||||||
RDSEON eon[4];
|
RDSEON eon[4];
|
||||||
} RDSData;
|
} RDSData;
|
||||||
|
typedef struct {
|
||||||
|
uint8_t af_state : 6;
|
||||||
|
} RDSEONState;
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t ps_update : 1;
|
uint8_t ps_update : 1;
|
||||||
uint8_t tps_update : 1;
|
uint8_t tps_update : 1;
|
||||||
@@ -148,6 +147,9 @@ typedef struct {
|
|||||||
|
|
||||||
uint8_t eon_index : 3;
|
uint8_t eon_index : 3;
|
||||||
uint8_t eon_state : 4;
|
uint8_t eon_state : 4;
|
||||||
|
RDSEONState eon_states[4];
|
||||||
|
|
||||||
|
uint8_t af_state : 6;
|
||||||
|
|
||||||
uint16_t last_stream0_group[3];
|
uint16_t last_stream0_group[3];
|
||||||
} RDSState;
|
} RDSState;
|
||||||
@@ -197,6 +199,7 @@ typedef struct {
|
|||||||
RDSEncoderData encoder_data;
|
RDSEncoderData encoder_data;
|
||||||
uint8_t program : 3;
|
uint8_t program : 3;
|
||||||
uint8_t file_ender; // Always is 95 my freq
|
uint8_t file_ender; // Always is 95 my freq
|
||||||
|
uint16_t crc;
|
||||||
} RDSEncoderFile;
|
} RDSEncoderFile;
|
||||||
|
|
||||||
#define GROUP_TYPE_0 ( 0 << 4)
|
#define GROUP_TYPE_0 ( 0 << 4)
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
|
|
||||||
#define RDS_DEVICE "RDS"
|
#define RDS_DEVICE "RDS"
|
||||||
|
|
||||||
#define NUM_MPX_FRAMES 256
|
#define NUM_MPX_FRAMES 128
|
||||||
|
|
||||||
static uint8_t stop_rds;
|
static uint8_t stop_rds;
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ static void *control_pipe_worker(void* modulator) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void show_version() {
|
static void show_version() {
|
||||||
printf("rds95 (a RDS encoder by radio95) version 1.2\n");
|
printf("rds95 (a RDS encoder by radio95) version %s\n", VERSION);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void show_help(char *name) {
|
static void show_help(char *name) {
|
||||||
@@ -43,7 +43,6 @@ static void show_help(char *name) {
|
|||||||
"Usage: %s [options]\n"
|
"Usage: %s [options]\n"
|
||||||
"\n"
|
"\n"
|
||||||
" -C,--ctl FIFO control pipe\n"
|
" -C,--ctl FIFO control pipe\n"
|
||||||
" -h,--help Show this help text and exit\n"
|
|
||||||
"\n",
|
"\n",
|
||||||
name
|
name
|
||||||
);
|
);
|
||||||
@@ -95,8 +94,8 @@ int main(int argc, char **argv) {
|
|||||||
format.rate = RDS_SAMPLE_RATE;
|
format.rate = RDS_SAMPLE_RATE;
|
||||||
|
|
||||||
buffer.prebuf = 0;
|
buffer.prebuf = 0;
|
||||||
buffer.tlength = 12228;
|
buffer.tlength = NUM_MPX_FRAMES*4;
|
||||||
buffer.maxlength = 12228;
|
buffer.maxlength = NUM_MPX_FRAMES*4;
|
||||||
|
|
||||||
rds_device = pa_simple_new(
|
rds_device = pa_simple_new(
|
||||||
NULL,
|
NULL,
|
||||||
|
|||||||
Reference in New Issue
Block a user