diff --git a/gen_wave.py b/gen_wave.py new file mode 100644 index 0000000..b1ca6bb --- /dev/null +++ b/gen_wave.py @@ -0,0 +1,92 @@ +import math +import io, os +import matplotlib.pyplot as plt + +sample_rate = 9500 + +# next 2 funcs are modified from ChristopheJacquet's pydemod +def rrcosfilter(NumSamples): + T_delta = 1/float(sample_rate) + sample_num = list(range(NumSamples)) + h_rrc = [0.0] * NumSamples + SymbolPeriod = 1/(2*1187.5) + + for x in sample_num: + t = (x-NumSamples/2)*T_delta + if t == 0.0: + h_rrc[x] = 1.0 - 1 + (4/math.pi) + elif t == SymbolPeriod/4: + 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)))) + elif t == -SymbolPeriod/4: + 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)))) + else: + h_rrc[x] = (4*(t/SymbolPeriod)*math.cos(math.pi*t*2/SymbolPeriod))/ \ + (math.pi*t*(1-(4*t/SymbolPeriod)*(4*t/SymbolPeriod))/SymbolPeriod) + + return h_rrc + +def convolve(a, b): + out = [0] * (len(a) + len(b) - 1) + for i in range(len(a)): + for j in range(len(b)): + out[i+j] += a[i] * b[j] + return out + +PATH = os.path.dirname(os.path.abspath(__file__)) + +outc = io.open(f"{PATH}/src/waveforms.c", mode="w", encoding="utf8") +outh = io.open(f"{PATH}/src/waveforms.h", mode="w", encoding="utf8") + +header = u""" +/* This file was automatically generated by "gen_wave.py". + (C) 2014 Christophe Jacquet. + (C) 2023 Anthony96922. + (C) 2025 kuba201. + Released under the GNU GPL v3 license. +*/ + +""" + +outc.write(header) +outh.write(header) + +def generate(): + offset = int(sample_rate*0.004) # 190 khz = 760 + count = int(offset / 10**(len(str(offset)) - 1)) # 760 / 100 = 7 + l = int(sample_rate / 1187.5) // 2 # 16/2 = 8 + if l == 1: raise Exception("Sample rate too small") + print(f"{offset=} {count=} {l=}") + + sample = [0.0] * (count*l) + sample[l] = 1 + sample[2*l] = -1 + + # Apply the data-shaping filter + sf = rrcosfilter(l*16) + shapedSamples = convolve(sample, sf) + + # Slice the array like numpy would + out = shapedSamples[offset-l*count:offset+l*count] + + plt.plot(sf, label="sf") + plt.plot(shapedSamples, label="shapedSamples") + plt.plot(out, label="out") + plt.legend() + plt.grid(True) + plt.show() + + outc.write(u"float waveform_biphase[{size}] = {{{values}}};\n\n".format( + values = u", ".join(map(str, [val / 2.5 for val in out])), + size = len(out))) + # note: need to limit the amplitude so as not to saturate when the biphase + # waveforms are summed + + outh.write(u"extern float waveform_biphase[{size}];\n".format(size=len(out))) + + +generate() + +outc.close() +outh.close() \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c425064..d4a85c8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,5 +1,3 @@ -# This is from kuba's version of MiniRDS - cmake_minimum_required(VERSION 3.10) # Project name and version @@ -8,10 +6,6 @@ project(minirds VERSION 1.0) # Define options option(ODA_RTP "Enable ODA (RT+)" ON) -option(STATIC_LIBSAMPLERATE "Use a static libsamplerate library (.a)" OFF) - -set(LIBSAMPLERATE_DIR "./libsamplerate" CACHE STRING "Directory for static libsamplerate") - # Set compiler and flags set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Werror -Wextra -pedantic -O2 -std=c18 -DVERSION=\"${PROJECT_VERSION}\"") @@ -29,9 +23,7 @@ endif() set(SOURCES ${SOURCES} - fm_mpx.c control_pipe.c - resampler.c modulator.c lib.c ascii_cmd.c @@ -40,20 +32,8 @@ set(SOURCES # Define the executable add_executable(minirds ${SOURCES}) -# Handle libsamplerate linkage -if(STATIC_LIBSAMPLERATE) - target_include_directories(minirds PRIVATE ${LIBSAMPLERATE_DIR}/include) - target_link_libraries(minirds PRIVATE ${LIBSAMPLERATE_DIR}/lib/libsamplerate.a) -else() - find_library(SAMPLERATE_LIBRARY samplerate) - target_link_libraries(minirds PRIVATE ${SAMPLERATE_LIBRARY}) -endif() - # Link additional libraries target_link_libraries(minirds PRIVATE m pthread pulse pulse-simple) # Install target -install(TARGETS minirds DESTINATION /usr/local/bin) -# Add profiling flags globally -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O2") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2") +install(TARGETS minirds DESTINATION /usr/local/bin) \ No newline at end of file diff --git a/src/ascii_cmd.c b/src/ascii_cmd.c index 11da238..b235145 100644 --- a/src/ascii_cmd.c +++ b/src/ascii_cmd.c @@ -1,23 +1,3 @@ -/* - * MiniRDS - FM RDS Encoder - * contains code from: - * mpxgen - FM multiplex encoder with Stereo and RDS - * Copyright (C) 2019 Anthony96922 - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - #include "common.h" #include "rds.h" #include "fm_mpx.h" @@ -50,28 +30,6 @@ void process_ascii_cmd(unsigned char *str) { } } - if (cmd_len > 4 && str[3] == ' ') { - cmd = str; - cmd[3] = 0; - arg = str + 4; - - if (CMD_MATCHES("MPX")) { - float gains[2]; - if (sscanf((char *)arg, "%f,%f", - &gains[0], &gains[1]) == 2) - { - set_carrier_volume(0, gains[0]); - set_carrier_volume(1, gains[1]); - } - return; - } - if (CMD_MATCHES("VOL")) { - arg[4] = 0; - set_output_volume(strtof((char *)arg, NULL)); - return; - } - } - if(cmd_len == 5) { cmd = str; if(CMD_MATCHES("AFCH=")) { @@ -316,25 +274,12 @@ void process_ascii_cmd(unsigned char *str) { cmd = str; cmd[5] = 0; arg = str + 6; - if (CMD_MATCHES("LEVEL")) { - uint8_t val = strtoul((char *)arg, NULL, 10); - val /= 255; - val *= 15; /* max value*/ - set_carrier_volume(1, val); - return; - } } if (cmd_len > 7 && str[6] == '=') { cmd = str; cmd[6] = 0; arg = str + 7; - if (CMD_MATCHES("RDSGEN")) { - arg[1] = 0; - set_rdsgen(strtoul((char *)arg, NULL, 10)); - return; - } - if (CMD_MATCHES("PTYNEN")) { arg[1] = 0; set_rds_ptyn_enabled(strtoul((char *)arg, NULL, 10)); @@ -353,14 +298,6 @@ void process_ascii_cmd(unsigned char *str) { cmd = str; cmd[5] = 0; arg = str + 6; - if (CMD_MATCHES("LEVEL")) { - uint8_t val = strtoul((char *)arg, NULL, 10); - val /= 255; - val *= 15; /* max value*/ - set_carrier_volume(1, val); - return; - } - if (CMD_MATCHES("ECCEN")) { set_rds_ecclic_toggle(arg[0]); return; diff --git a/src/ascii_cmd.h b/src/ascii_cmd.h index fbcf2a6..d999aef 100644 --- a/src/ascii_cmd.h +++ b/src/ascii_cmd.h @@ -1,21 +1,3 @@ -/* - * mpxgen - FM multiplex encoder with Stereo and RDS - * Copyright (C) 2019 Anthony96922 - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - #define CMD_BUFFER_SIZE 255 #define CTL_BUFFER_SIZE (CMD_BUFFER_SIZE * 2) #define READ_TIMEOUT_MS 100 diff --git a/src/common.h b/src/common.h index f6ec959..01734b8 100644 --- a/src/common.h +++ b/src/common.h @@ -1,22 +1,4 @@ -/* - * mpxgen - FM multiplex encoder with Stereo and RDS - * Copyright (C) 2019-2021 Anthony96922 - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -/* common includes for mpxgen */ +/* common includes for rds95 */ #include #include #include diff --git a/src/control_pipe.c b/src/control_pipe.c index babfad5..5bd07cf 100644 --- a/src/control_pipe.c +++ b/src/control_pipe.c @@ -1,21 +1,3 @@ -/* - * mpxgen - FM multiplex encoder with Stereo and RDS - * Copyright (C) 2019 Anthony96922 - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - #include "common.h" #include "ascii_cmd.h" #include "control_pipe.h" diff --git a/src/control_pipe.h b/src/control_pipe.h index 7f6b2d6..2fec9c9 100644 --- a/src/control_pipe.h +++ b/src/control_pipe.h @@ -1,21 +1,3 @@ -/* - * mpxgen - FM multiplex encoder with Stereo and RDS - * Copyright (C) 2019 Anthony96922 - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - #include #include #include diff --git a/src/fm_mpx.c b/src/fm_mpx.c deleted file mode 100644 index ec14da4..0000000 --- a/src/fm_mpx.c +++ /dev/null @@ -1,70 +0,0 @@ -/* - * mpxgen - FM multiplex encoder with Stereo and RDS - * Copyright (C) 2019 Anthony96922 - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "common.h" - -#include "rds.h" -#include "fm_mpx.h" - -static float mpx_vol; - -static uint8_t rdsgen; - -void set_output_volume(float vol) { - if (vol > 100.0f) vol = 100.0f; - mpx_vol = vol / 100.0f; -} - -void set_rdsgen(uint8_t gen) { - if (gen > 1) gen = 1; - rdsgen = gen; -} - -void set_carrier_volume(uint8_t carrier, float new_volume) { - (void)carrier; - mpx_vol = new_volume / 100.0f; -} - -void fm_mpx_init(uint32_t sample_rate) { - (void)sample_rate; - rdsgen = 1; -} - -void fm_rds_get_frames(float *outbuf, size_t num_frames) { - size_t j = 0; - float out; - - for (size_t i = 0; i < num_frames; i++) { - out = 0.0f; - - out += get_rds_sample(0); - - /* clipper */ - out = fminf(+1.0f, out); - out = fmaxf(-1.0f, out); - - /* adjust volume and put into channel */ - if(rdsgen != 0) outbuf[j] = out * mpx_vol; - j++; - - } -} - -void fm_mpx_exit() { - return; -} diff --git a/src/fm_mpx.h b/src/fm_mpx.h deleted file mode 100644 index 61180be..0000000 --- a/src/fm_mpx.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * mpxgen - FM multiplex encoder with Stereo and RDS - * Copyright (C) 2019 Anthony96922 - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -/* MPX */ -#define NUM_MPX_FRAMES_IN 512 -#define NUM_MPX_FRAMES_OUT (NUM_MPX_FRAMES_IN * 2) -/* - * The sample rate at which the MPX generation runs at - */ -#define MPX_SAMPLE_RATE RDS_SAMPLE_RATE - -#define OUTPUT_SAMPLE_RATE MPX_SAMPLE_RATE - -extern void fm_mpx_init(uint32_t sample_rate); -extern void fm_rds_get_frames(float *outbuf, size_t num_frames); -extern void fm_mpx_exit(); -extern void set_output_volume(float vol); -extern void set_rdsgen(uint8_t gen); -extern void set_carrier_volume(uint8_t carrier, float new_volume); diff --git a/src/lib.c b/src/lib.c index 966af57..6a07d1d 100644 --- a/src/lib.c +++ b/src/lib.c @@ -1,33 +1,7 @@ -/* - * mpxgen - FM multiplex encoder with Stereo and RDS - * Copyright (C) 2021 Anthony96922 - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - #include "common.h" #include "rds.h" #include -/* - * Stuff for RDS - * - */ - -/* needed workaround implicit declaration (edit: do we need this? ) */ -extern int nanosleep(const struct timespec *req, struct timespec *rem); - /* millisecond sleep */ void msleep(unsigned long ms) { struct timespec ts; @@ -158,22 +132,6 @@ static uint16_t offset_words[] = { 0x350 /* C' */ }; -/* CRC-16 ITU-T/CCITT checkword calculation */ -/* wanna ask where is this used */ -uint16_t crc16(uint8_t *data, size_t len) { - uint16_t crc = 0xffff; - - for (size_t i = 0; i < len; i++) { - crc = (crc >> 8) | (crc << 8); - crc ^= data[i]; - crc ^= (crc & 0xff) >> 4; - crc ^= (crc << 8) << 4; - crc ^= ((crc & 0xff) << 4) << 1; - } - - return crc ^ 0xffff; -} - /* Calculate the checkword for each block and emit the bits */ void add_checkwords(uint16_t *blocks, uint8_t *bits) { diff --git a/src/lib.h b/src/lib.h index cafbba4..c7008bf 100644 --- a/src/lib.h +++ b/src/lib.h @@ -1,21 +1,3 @@ -/* - * mpxgen - FM multiplex encoder with Stereo and RDS - * Copyright (C) 2021 Anthony96922 - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - extern void msleep(unsigned long ms); extern size_t _strnlen(const char *s, size_t maxlen); @@ -26,5 +8,4 @@ extern char *get_rtp_tag_name(uint8_t rtp_tag); extern void add_checkwords(uint16_t *blocks, uint8_t *bits); extern uint8_t add_rds_af(struct rds_af_t *af_list, float freq); extern char *show_af_list(struct rds_af_t af_list); -extern uint16_t crc16(uint8_t *data, size_t len); extern unsigned char *xlat(unsigned char *str); \ No newline at end of file diff --git a/src/minirds.c b/src/minirds.c index d441c6f..822015b 100644 --- a/src/minirds.c +++ b/src/minirds.c @@ -29,6 +29,8 @@ #include "lib.h" #include "ascii_cmd.h" +#define NUM_MPX_FRAMES 512 + static uint8_t stop_rds; static void stop() { @@ -94,8 +96,6 @@ int main(int argc, char **argv) { .ecc = 0xE2, .lps = "radio95 - Radio Nowotomyskie" }; - float volume = 100.0f; - /* buffers */ float *mpx_buffer; int8_t r; @@ -208,23 +208,19 @@ done_parsing_opts: pthread_attr_init(&attr); /* Setup buffers */ - mpx_buffer = malloc(NUM_MPX_FRAMES_IN * 2 * sizeof(float)); + mpx_buffer = malloc(NUM_MPX_FRAMES * 2 * sizeof(float)); /* Gracefully stop the encoder on SIGINT or SIGTERM */ signal(SIGINT, stop); signal(SIGTERM, stop); - /* Initialize the baseband generator */ - fm_mpx_init(MPX_SAMPLE_RATE); - set_output_volume(volume); - /* Initialize the RDS modulator */ init_rds_encoder(rds_params); /* PASIMPLE format */ format.format = PA_SAMPLE_FLOAT32NE; format.channels = 1; - format.rate = OUTPUT_SAMPLE_RATE; + format.rate = RDS_SAMPLE_RATE; device = pa_simple_new( NULL, // Default PulseAudio server @@ -264,10 +260,12 @@ done_parsing_opts: int pulse_error; for (;;) { - fm_rds_get_frames(mpx_buffer, NUM_MPX_FRAMES_IN); + for (size_t i = 0; i < NUM_MPX_FRAMES; i++) { + mpx_buffer[i] = fminf(1.0f, fmaxf(-1.0f, get_rds_sample())); + } /* num_bytes = audio frames( * channels) * bytes per sample */ - if (pa_simple_write(device, mpx_buffer, NUM_MPX_FRAMES_IN * sizeof(float), &pulse_error) != 0) { + if (pa_simple_write(device, mpx_buffer, NUM_MPX_FRAMES * sizeof(float), &pulse_error) != 0) { fprintf(stderr, "Error: could not play audio. (%s : %d)\n", pa_strerror(pulse_error), pulse_error); break; } @@ -288,7 +286,6 @@ exit: pthread_attr_destroy(&attr); pa_simple_free(device); - fm_mpx_exit(); exit_rds_encoder(); free(mpx_buffer); diff --git a/src/modulator.c b/src/modulator.c index cb30b46..7ceb9e1 100644 --- a/src/modulator.c +++ b/src/modulator.c @@ -1,46 +1,22 @@ -/* - * mpxgen - FM multiplex encoder with Stereo and RDS - * Copyright (C) 2021 Anthony96922 - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - #include "common.h" #include "rds.h" #include "fm_mpx.h" #include "waveforms.h" #include "modulator.h" -static struct rds_t **rds_ctx; +static struct rds_t *rds; static float **waveform; -/* - * Create the RDS objects - * - */ void init_rds_objects() { - rds_ctx = malloc(sizeof(struct rds_t)); - - rds_ctx[0] = malloc(sizeof(struct rds_t)); - rds_ctx[0]->bit_buffer = malloc(BITS_PER_GROUP); - rds_ctx[0]->sample_buffer = + rds = malloc(sizeof(struct rds_t)); + rds->bit_buffer = malloc(BITS_PER_GROUP); + rds->sample_buffer = malloc(SAMPLE_BUFFER_SIZE * sizeof(float)); - rds_ctx[0]->symbol_shift_buf_idx = 0; waveform = malloc(2 * sizeof(float)); for (uint8_t i = 0; i < 2; i++) { + // This is the bpsk, so waveform[0] is 0 degrees out of phase but waveform[1] is 180 degrees, bpsk waveform[i] = malloc(FILTER_SIZE * sizeof(float)); for (uint16_t j = 0; j < FILTER_SIZE; j++) { waveform[i][j] = i ? @@ -50,14 +26,9 @@ void init_rds_objects() { } void exit_rds_objects() { - int has_symbol_shift = rds_ctx[0]->symbol_shift; - if (has_symbol_shift) { - free(rds_ctx[0]->symbol_shift_buf); - } - free(rds_ctx[0]->sample_buffer); - free(rds_ctx[0]->bit_buffer); - free(rds_ctx[0]); - free(rds_ctx); + free(rds->sample_buffer); + free(rds->bit_buffer); + free(rds); for (uint8_t i = 0; i < 2; i++) { free(waveform[i]); @@ -69,46 +40,33 @@ void exit_rds_objects() { /* Get an RDS sample. This generates the envelope of the waveform using * pre-generated elementary waveform samples. */ -float get_rds_sample(uint8_t stream_num) { - struct rds_t *rds; +float get_rds_sample() { uint16_t idx; float *cur_waveform; float sample; - /* select context */ - rds = rds_ctx[stream_num]; - if (rds->sample_count == SAMPLES_PER_BIT) { + // New Sample if (rds->bit_pos == BITS_PER_GROUP) { + // New bit stream get_rds_bits(rds->bit_buffer); rds->bit_pos = 0; } - /* do differential encoding */ + // Differentially encode, so 1111 becomes 1000 and 0001 becomes 0001 rds->cur_bit = rds->bit_buffer[rds->bit_pos++]; rds->prev_output = rds->cur_output; rds->cur_output = rds->prev_output ^ rds->cur_bit; idx = rds->in_sample_index; - cur_waveform = waveform[rds->cur_output]; + cur_waveform = waveform[rds->cur_output]; // get the waveform, this is the biphase in a 0/180 degree phase shift + + // Copy over the biphase to the sample buffer, every SAMPLES_PER_BIT + for (uint16_t i = 0; i < FILTER_SIZE; i++) { + rds->sample_buffer[idx++] += *cur_waveform++; + if (idx == SAMPLE_BUFFER_SIZE) idx = 0; + } - uint16_t buffer_remaining = SAMPLE_BUFFER_SIZE - idx; - if (buffer_remaining >= FILTER_SIZE) { - // Process continuous chunk - for (uint16_t i = 0; i < FILTER_SIZE; i++) { - rds->sample_buffer[idx + i] += cur_waveform[i]; - } - idx += FILTER_SIZE; - } else { - // Process in two parts to handle wrapping - for (uint16_t i = 0; i < buffer_remaining; i++) { - rds->sample_buffer[idx + i] += cur_waveform[i]; - } - for (uint16_t i = 0; i < FILTER_SIZE - buffer_remaining; i++) { - rds->sample_buffer[i] += cur_waveform[buffer_remaining + i]; - } - idx = FILTER_SIZE - buffer_remaining; - } rds->in_sample_index += SAMPLES_PER_BIT; if (rds->in_sample_index == SAMPLE_BUFFER_SIZE) rds->in_sample_index = 0; @@ -117,20 +75,8 @@ float get_rds_sample(uint8_t stream_num) { } rds->sample_count++; - if (rds->symbol_shift) { - rds->symbol_shift_buf[rds->symbol_shift_buf_idx++] = - rds->sample_buffer[rds->out_sample_index]; - - if (rds->symbol_shift_buf_idx == rds->symbol_shift) - rds->symbol_shift_buf_idx = 0; - - sample = rds->symbol_shift_buf[rds->symbol_shift_buf_idx]; - - goto done; - } - sample = rds->sample_buffer[rds->out_sample_index]; -done: + rds->sample_buffer[rds->out_sample_index++] = 0; if (rds->out_sample_index == SAMPLE_BUFFER_SIZE) rds->out_sample_index = 0; diff --git a/src/modulator.h b/src/modulator.h index db34124..fa2d395 100644 --- a/src/modulator.h +++ b/src/modulator.h @@ -1,21 +1,3 @@ -/* - * mpxgen - FM multiplex encoder with Stereo and RDS - * Copyright (C) 2021 Anthony96922 - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - /* RDS signal context */ typedef struct rds_t { uint8_t *bit_buffer; /* BITS_PER_GROUP */ @@ -27,9 +9,6 @@ typedef struct rds_t { uint8_t sample_count; uint16_t in_sample_index; uint16_t out_sample_index; - uint8_t symbol_shift; - float *symbol_shift_buf; - uint8_t symbol_shift_buf_idx; } rds_t; extern void init_rds_objects(); diff --git a/src/rds.c b/src/rds.c index a031c9d..1621a50 100644 --- a/src/rds.c +++ b/src/rds.c @@ -1,21 +1,3 @@ -/* - * mpxgen - FM multiplex encoder with Stereo and RDS - * Copyright (C) 2019 Anthony96922 - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - #include "common.h" #include "rds.h" #include "modulator.h" diff --git a/src/rds.h b/src/rds.h index 39b1f54..fe3d099 100644 --- a/src/rds.h +++ b/src/rds.h @@ -1,21 +1,3 @@ -/* - * mpxgen - FM multiplex encoder with Stereo and RDS - * Copyright (C) 2019 Anthony96922 - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - #ifndef RDS_H #define RDS_H @@ -29,7 +11,7 @@ #define GROUP_LENGTH 4 #define BITS_PER_GROUP (GROUP_LENGTH * (BLOCK_SIZE + POLY_DEG)) #define RDS_SAMPLE_RATE 9500 -#define SAMPLES_PER_BIT 8 +#define SAMPLES_PER_BIT 8 #define FILTER_SIZE 24 #define SAMPLE_BUFFER_SIZE (SAMPLES_PER_BIT + FILTER_SIZE) @@ -310,7 +292,7 @@ extern void set_rds_tp(uint8_t tp); extern void set_rds_ms(uint8_t ms); extern void set_rds_ct(uint8_t ct); extern void set_rds_di(uint8_t di); -extern float get_rds_sample(uint8_t stream_num); +extern float get_rds_sample(); extern uint16_t get_rds_pi(); extern void set_rds_cg(uint16_t* blocks); diff --git a/src/resampler.c b/src/resampler.c deleted file mode 100644 index 668a7cd..0000000 --- a/src/resampler.c +++ /dev/null @@ -1,52 +0,0 @@ -/* - * mpxgen - FM multiplex encoder with Stereo and RDS - * Copyright (C) 2019 Anthony96922 - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "common.h" -#include "resampler.h" - -int8_t resampler_init(SRC_STATE **src_state, uint8_t channels) { - int src_error; - - *src_state = src_new(CONVERTER_TYPE, channels, &src_error); - - if (*src_state == NULL) { - fprintf(stderr, "Error: src_new failed: %s\n", src_strerror(src_error)); - return -1; - } - - return 0; -} - -int8_t resample(SRC_STATE *src_state, SRC_DATA src_data, size_t *frames_generated) { - int src_error; - - src_error = src_process(src_state, &src_data); - - if (src_error) { - fprintf(stderr, "Error: src_process failed: %s\n", src_strerror(src_error)); - return -1; - } - - *frames_generated = src_data.output_frames_gen; - - return 0; -} - -void resampler_exit(SRC_STATE *src_state) { - src_delete(src_state); -} diff --git a/src/resampler.h b/src/resampler.h deleted file mode 100644 index 3be9c47..0000000 --- a/src/resampler.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * mpxgen - FM multiplex encoder with Stereo and RDS - * Copyright (C) 2019 Anthony96922 - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include - -#define CONVERTER_TYPE SRC_SINC_MEDIUM_QUALITY - -extern int8_t resampler_init(SRC_STATE **src_state, uint8_t channels); -extern int8_t resample(SRC_STATE *src_state, SRC_DATA src_data, size_t *frames_generated); -extern void resampler_exit(SRC_STATE *src_state); diff --git a/src/supported_cmds.txt b/src/supported_cmds.txt index 4251376..6f3cf56 100644 --- a/src/supported_cmds.txt +++ b/src/supported_cmds.txt @@ -13,8 +13,6 @@ RT1EN TA TP CT -LEVEL - Not redecommended, if possible use `MPX` -RDSGEN ECC ECCEN G diff --git a/src/uecp_rds_todo.txt b/src/uecp_rds_todo.txt deleted file mode 100644 index fcfc7d4..0000000 --- a/src/uecp_rds_todo.txt +++ /dev/null @@ -1,21 +0,0 @@ -This aims to have the around the same options as UECP should have, this is what is not yet implemented: -- PIN (low priority, how do i decode that?) -- EON AF (very low priority) -- Linkage information (very low priority) -- (ODA skipped) -- TDC (low priority) -- EWS (how do i even implement that? can someone give docs?) -- IH (low priority) -- (Paging skipped) -- (No CT time adjustments) -- RDS on/off (low priority) -- RDS phase (might be medium, 9.5 degree steps) -- (ARI skipped) - -Others: -- Encoder identification (site and encoder address) -- (PSN skipped) -- (EON skipped) -- Data set select -- Group sequence -- (other sequence related comamnds skipped) diff --git a/src/waveforms.c b/src/waveforms.c index d615df6..78e2b96 100644 --- a/src/waveforms.c +++ b/src/waveforms.c @@ -1,7 +1,8 @@ /* This file was automatically generated by "gen_wave.py". (C) 2014 Christophe Jacquet. - Modified by kuba201 + (C) 2023 Anthony96922. + (C) 2025 kuba201. Released under the GNU GPL v3 license. */ diff --git a/src/waveforms.h b/src/waveforms.h index 67bab7e..c657cad 100644 --- a/src/waveforms.h +++ b/src/waveforms.h @@ -1,7 +1,8 @@ /* This file was automatically generated by "gen_wave.py". (C) 2014 Christophe Jacquet. - Modified by kuba201 + (C) 2023 Anthony96922. + (C) 2025 kuba201. Released under the GNU GPL v3 license. */ diff --git a/waveform/Pydemod/README.md b/waveform/Pydemod/README.md deleted file mode 100644 index 73ccf90..0000000 --- a/waveform/Pydemod/README.md +++ /dev/null @@ -1,11 +0,0 @@ -Pydemod -------- - -Pydemod is a set of Python libraries and tools for demodulating radio signals. It does not intend to compete with full-featured packages such as GNU Radio. Instead, it strives to allow radio enthusiasts to gain hands-on experience with modulation schemes. - -Pydemod relies on [NumPy](http://numpy.scipy.org/)/[SciPy](http://www.scipy.org/). - -Pydemod is licensed under the terms of the [GNU GPL v3](https://www.gnu.org/copyleft/gpl.html). - ----- -_Pydemod is developed by [Christophe Jacquet](http://www.jacquet80.eu/), [F8FTK](http://f8ftk.tk)._ diff --git a/waveform/Pydemod/src/gen_wave.py b/waveform/Pydemod/src/gen_wave.py deleted file mode 100644 index ff10395..0000000 --- a/waveform/Pydemod/src/gen_wave.py +++ /dev/null @@ -1,81 +0,0 @@ -#!/usr/bin/python - - -# PiFmRds - FM/RDS transmitter for the Raspberry Pi -# Copyright (C) 2014 Christophe Jacquet, F8FTK -# -# See https://github.com/ChristopheJacquet/PiFmRds -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -# This program generates the waveform of a single biphase symbol -# -# This program uses Pydemod, see https://github.com/ChristopheJacquet/Pydemod - -import pydemod.app.rds as rds -import numpy -import io -import matplotlib.pyplot as plt - -sample_rate = 9500 - -outc = io.open("waveforms.c", mode="w", encoding="utf8") -outh = io.open("waveforms.h", mode="w", encoding="utf8") - -header = u""" -/* This file was automatically generated by "gen_wave.py". - (C) 2014 Christophe Jacquet. - Modified by kuba201 - Released under the GNU GPL v3 license. -*/ - -""" - -outc.write(header) -outh.write(header) - -def generate_bit(name): - offset = int(sample_rate*0.004) # 190 khz = 760 - count = int(offset / 10**(len(str(offset)) - 1)) # 760 / 100 = 7 - l = int(sample_rate / 1187.5) // 2 # 16/2 = 8 - if l == 1: raise Exception("Sample rate too small") - - sample = numpy.zeros(count*l) - sample[l] = 1 - sample[2*l] = -1 - - # Apply the data-shaping filter - sf = rds.pulse_shaping_filter(l*16, sample_rate-1) - shapedSamples = numpy.convolve(sample, sf) - - out = shapedSamples[offset-l*count:offset+l*count] #[offset:offset+l*count] - #plt.plot(sf) - #plt.plot(shapedSamples) - plt.plot(out) - plt.show() - - outc.write(u"float waveform_{name}[{size}] = {{{values}}};\n\n".format( - name = name, - values = u", ".join(map(str, out / 2.5)), - size = len(out))) - # note: need to limit the amplitude so as not to saturate when the biphase - # waveforms are summed - - outh.write(u"extern float waveform_{name}[{size}];\n".format(name=name, size=len(out))) - - -generate_bit("biphase") - -outc.close() -outh.close() \ No newline at end of file diff --git a/waveform/Pydemod/src/pydemod/__init__.py b/waveform/Pydemod/src/pydemod/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/waveform/Pydemod/src/pydemod/app/__init__.py b/waveform/Pydemod/src/pydemod/app/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/waveform/Pydemod/src/pydemod/app/rds.py b/waveform/Pydemod/src/pydemod/app/rds.py deleted file mode 100644 index 2d45dd9..0000000 --- a/waveform/Pydemod/src/pydemod/app/rds.py +++ /dev/null @@ -1,11 +0,0 @@ -# This file is part of Pydemod -# Copyright Christophe Jacquet (F8FTK), 2014 -# Licence: GNU GPL v3 -# See: https://github.com/ChristopheJacquet/Pydemod - -import numpy - -import pydemod.filters.shaping as shaping - -def pulse_shaping_filter(length, sample_rate): - return shaping.rrcosfilter(length, 1, 1/(2*1187.5), sample_rate+1) [1] \ No newline at end of file diff --git a/waveform/Pydemod/src/pydemod/filters/__init__.py b/waveform/Pydemod/src/pydemod/filters/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/waveform/Pydemod/src/pydemod/filters/shaping.py b/waveform/Pydemod/src/pydemod/filters/shaping.py deleted file mode 100644 index 29766d6..0000000 --- a/waveform/Pydemod/src/pydemod/filters/shaping.py +++ /dev/null @@ -1,64 +0,0 @@ -# This file is part of Pydemod -# Copyright Christophe Jacquet (F8FTK), 2014 -# Licence: GNU GPL v3 -# See: https://github.com/ChristopheJacquet/Pydemod -# -# Contains code from CommPy, used under the terms of the GPL -# https://github.com/veeresht/CommPy/blob/master/commpy/filters.py -# (c) 2012 Veeresh Taranalli - -import numpy - -# The following function is from: -# https://github.com/veeresht/CommPy/blob/master/commpy/filters.py -# See also: https://en.wikipedia.org/wiki/Root-raised-cosine_filter -def rrcosfilter(N, alpha, Ts, Fs): - """ - Generates a root raised cosine (RRC) filter (FIR) impulse response. - - Parameters - ---------- - N : int - Length of the filter in samples. - - alpha: float - Roll off factor (Valid values are [0, 1]). - - Ts : float - Symbol period in seconds. - - Fs : float - Sampling Rate in Hz. - - Returns - --------- - - h_rrc : 1-D ndarray of floats - Impulse response of the root raised cosine filter. - - time_idx : 1-D ndarray of floats - Array containing the time indices, in seconds, for - the impulse response. - """ - - T_delta = 1/float(Fs) - time_idx = ((numpy.arange(N)-N/2))*T_delta - sample_num = numpy.arange(N) - h_rrc = numpy.zeros(N, dtype=float) - - for x in sample_num: - t = (x-N/2)*T_delta - if t == 0.0: - h_rrc[x] = 1.0 - alpha + (4*alpha/numpy.pi) - elif alpha != 0 and t == Ts/(4*alpha): - h_rrc[x] = (alpha/numpy.sqrt(2))*(((1+2/numpy.pi)* \ - (numpy.sin(numpy.pi/(4*alpha)))) + ((1-2/numpy.pi)*(numpy.cos(numpy.pi/(4*alpha))))) - elif alpha != 0 and t == -Ts/(4*alpha): - h_rrc[x] = (alpha/numpy.sqrt(2))*(((1+2/numpy.pi)* \ - (numpy.sin(numpy.pi/(4*alpha)))) + ((1-2/numpy.pi)*(numpy.cos(numpy.pi/(4*alpha))))) - else: - h_rrc[x] = (numpy.sin(numpy.pi*t*(1-alpha)/Ts) + \ - 4*alpha*(t/Ts)*numpy.cos(numpy.pi*t*(1+alpha)/Ts))/ \ - (numpy.pi*t*(1-(4*alpha*t/Ts)*(4*alpha*t/Ts))/Ts) - - return time_idx, h_rrc diff --git a/waveform/Pydemod/src/waveforms.c b/waveform/Pydemod/src/waveforms.c deleted file mode 100644 index f91b354..0000000 --- a/waveform/Pydemod/src/waveforms.c +++ /dev/null @@ -1,8 +0,0 @@ - -/* This file was automatically generated by "generate_waveforms.py". - (C) 2014 Christophe Jacquet. - Released under the GNU GPL v3 license. -*/ - -float waveform_biphase[24] = {0.002532628775852384, -6.480266785053461e-18, -0.004522551385450683, 2.807603181808536e-17, 0.00940690688173742, -2.1836329850641248e-17, -0.025868993924777907, -1.1701083086090883e-17, 0.1552139635486674, 0.4, 0.5432488724203361, 0.4, 0.0, -0.4, -0.5432488724203361, -0.4, -0.1552139635486674, 1.1701083086090883e-17, 0.025868993924777907, 2.1836329850641248e-17, -0.00940690688173742, -2.807603181808536e-17, 0.004522551385450683, 6.480266785053461e-18}; - diff --git a/waveform/Pydemod/src/waveforms.h b/waveform/Pydemod/src/waveforms.h deleted file mode 100644 index 17eae23..0000000 --- a/waveform/Pydemod/src/waveforms.h +++ /dev/null @@ -1,7 +0,0 @@ - -/* This file was automatically generated by "generate_waveforms.py". - (C) 2014 Christophe Jacquet. - Released under the GNU GPL v3 license. -*/ - -extern float waveform_biphase[24];