mirror of
https://github.com/radio95-rnt/rds95.git
synced 2026-02-27 04:43:52 +01:00
remove rds2
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": 1728395259887,
|
"time": 1741628226423,
|
||||||
"version": "0.0.3"
|
"version": "0.0.3"
|
||||||
}
|
}
|
||||||
125
README.md
125
README.md
@@ -1,125 +0,0 @@
|
|||||||
# MiniRDS
|
|
||||||
|
|
||||||
This program is designed for generating a realtime RDS signal. It is capable of RDS2 using up to 3 additional subcarriers.
|
|
||||||
|
|
||||||
This is based on the RDS encoder from [Mpxgen](https://github.com/Anthony96922/mpxgen), which is currently not maintained.
|
|
||||||
|
|
||||||
Note that some missing groups were added from [librds](https://flerken.zapto.org:1115/kuba/librds), [my](https://flerken.zapto.org:1115/kuba) other project
|
|
||||||
|
|
||||||
#### Features
|
|
||||||
- Low resource requirements
|
|
||||||
- Support for basic RDS data fields: PS, RT, PTY and AF
|
|
||||||
- RDS items can be updated through control pipe
|
|
||||||
- RT+ support
|
|
||||||
- RDS2 support (including station logo transmission)
|
|
||||||
|
|
||||||
#### To do
|
|
||||||
- Threading
|
|
||||||
|
|
||||||
#### Planned features
|
|
||||||
- UECP
|
|
||||||
- Configuration file
|
|
||||||
|
|
||||||
## What are the changes from normal MiniRDS?
|
|
||||||
- CMake
|
|
||||||
- better arguments
|
|
||||||
- Option to disable ODA
|
|
||||||
- Better Group Sequence (original had 0A 2A but here it is 0A 0A 0A 0A 2A 2A 2A 2A (2A) so PS appear instantly and RT is fast)
|
|
||||||
- Added LIC and PIN
|
|
||||||
- Most commands are compatible with the ASCII format that pira.cz uses
|
|
||||||
- Custom groups (use `G=BBBBCCCCDDDD` or `CG AAAA BBBB CCCC DDDD`)
|
|
||||||
- Mono! mono? Mono! Not stereo! why? because the 2 channels were exactly the same in the first place so there was no point in having a 2nd one
|
|
||||||
|
|
||||||
## Build
|
|
||||||
For Debian-like distros: `sudo apt-get install libao-dev libsamplerate0-dev` to install deps.
|
|
||||||
|
|
||||||
Note that MiniRDS requires CMake to compile (install with `sudo apt install cmake`)
|
|
||||||
|
|
||||||
Once those are installed, run
|
|
||||||
```sh
|
|
||||||
git clone https://flerken.zapto.org:1115/kuba/minirds
|
|
||||||
cd minirds/src
|
|
||||||
mkdir build
|
|
||||||
cd build
|
|
||||||
cmake ..
|
|
||||||
make
|
|
||||||
sudo make install
|
|
||||||
```
|
|
||||||
|
|
||||||
## How to use
|
|
||||||
Simply run:
|
|
||||||
```
|
|
||||||
minirds
|
|
||||||
```
|
|
||||||
to confirm proper operation.
|
|
||||||
|
|
||||||
Please see `-h` for more options.
|
|
||||||
|
|
||||||
### Stereo Tool integration
|
|
||||||
The following setup allows MiniRDS to be used alongside Stereo Tool audio processor.
|
|
||||||
```
|
|
||||||
.-------------.
|
|
||||||
| Stereo Tool |--(FM MPX w/o RDS)-----.
|
|
||||||
'-------------' |
|
|
||||||
v
|
|
||||||
.-----------------------.
|
|
||||||
| ALSA dmixer |
|
|
||||||
| slave: MPX sound card |--------(to sound card)-------->
|
|
||||||
| 192 kHz |
|
|
||||||
'-----------------------'
|
|
||||||
^
|
|
||||||
.---------. |
|
|
||||||
| MiniRDS |--(RDS output)-------------'
|
|
||||||
'---------'
|
|
||||||
```
|
|
||||||
|
|
||||||
First, add the following contents to ~/.asoundrc:
|
|
||||||
```
|
|
||||||
# ST MPX output
|
|
||||||
pcm.mpxmix {
|
|
||||||
type dmix
|
|
||||||
ipc_key 1001
|
|
||||||
slave {
|
|
||||||
pcm "digital-out" # change to your actual sound card
|
|
||||||
rate 192000
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Next, add the collowing contents to ~/.ao:
|
|
||||||
```
|
|
||||||
dev=mpxmix
|
|
||||||
```
|
|
||||||
|
|
||||||
Then set the Stereo Tool MPX output to use the ALSA "mpxmix" output. Finally run minirds. *Adjust volumes accordingly.*
|
|
||||||
|
|
||||||
Note that this setup is not optimal. Hans plans to add RDS2 passthough to the ST external RDS input. [Stereo Tool forum post](https://forums.stereotool.com/viewtopic.php?f=14&t=33793&start=150)
|
|
||||||
|
|
||||||
If you need to you also can run MiniRDS on a ST transmitter modulated as mono audio, which does work however on some TXs the RDS signal may not be great due to harmonics of ST Pilot Tone
|
|
||||||
|
|
||||||
### Changing PS, RT, TA and PTY at run-time
|
|
||||||
You can control PS, RT, TA (Traffic Announcement flag), PTY (Program Type) and many other items at run-time using a named pipe (FIFO). For this run MiniRDS with the `--ctl` argument.
|
|
||||||
|
|
||||||
Scripts can be written to obtain and send "now playing" text data to MiniRDS for dynamic RDS.
|
|
||||||
|
|
||||||
See the [command list](doc/command_list.md) for a complete list of valid commands.
|
|
||||||
|
|
||||||
### RDS2
|
|
||||||
MiniRDS has a working implementation of the RFT protocol in RDS2. Please edit the Makefile accordingly and rebuild for RDS2 capabilities. You may use your own image by using the provided "make-station-logo.sh" script. Valid formats are PNG or JPG and should be about 3kB or less. Larger images take considerably longer to receive.
|
|
||||||
|
|
||||||
## References
|
|
||||||
- [EN 50067, Specification of the radio data system (RDS) for VHF/FM sound broadcasting in the frequency range 87.5 to 108.0 MHz](http://www.interactive-radio-system.com/docs/EN50067_RDS_Standard.pdf)
|
|
||||||
- [IEC 62106-2, Radio data system (RDS) – Part 2: Message format: coding and definitions of RDS features](http://downloads.dxing.si/download.php?file=ISO%20Stamdards/RDS/latest%20(includes%20RDS2)/iec-62106-2-2021.pdf)
|
|
||||||
- [IEC 62106-3, Radio data system (RDS) – Part 3: Usage and registration of Open Data Applications (ODAs)](http://downloads.dxing.si/download.php?file=ISO%20Stamdards/RDS/latest%20(includes%20RDS2)/iec-62106-3-2018.pdf)
|
|
||||||
- [IEC 62106-4, Radio data system (RDS) – Part 4: Registered code tables](http://downloads.dxing.si/download.php?file=ISO%20Stamdards/RDS/latest%20(includes%20RDS2)/iec-62106-4-2018.pdf)
|
|
||||||
- [IEC 62106-6, Radio data system (RDS) – Part 6: Compilation of technical specifications for Open Data Applications in the
|
|
||||||
public domain](http://downloads.dxing.si/download.php?file=ISO%20Stamdards/RDS/latest%20(includes%20RDS2)/iec-62106-6-2018.pdf)
|
|
||||||
- [P232 RDS Encoder
|
|
||||||
Technical Manual](https://pira.cz/rds/p232man.pdf)
|
|
||||||
- [PIRA32 RDS Encoder
|
|
||||||
Technical Manual](https://pira.cz/rds/manual.pdf)
|
|
||||||
|
|
||||||
## Credits
|
|
||||||
Note that this is a fork of [Anthony96922](https://github.com/Anthony96922)'s [MiniRDS](https://github.com/Anthony96922/MiniRDS)
|
|
||||||
|
|
||||||
The RDS waveform generator was adapted from [PiFmRds](https://github.com/ChristopheJacquet/PiFmRds)
|
|
||||||
@@ -8,14 +8,8 @@ project(minirds VERSION 1.0)
|
|||||||
# Define options
|
# Define options
|
||||||
option(ODA_RTP "Enable ODA (RT+)" ON)
|
option(ODA_RTP "Enable ODA (RT+)" ON)
|
||||||
|
|
||||||
option(RDS2 "Enable RDS2 capabilities" OFF)
|
|
||||||
option(RDS2_QUADRATURE_CARRIER "Shift RDS2 stream carriers by 90, 180, and 270 degrees" ON)
|
|
||||||
option(RDS2_SYMBOL_SHIFTING "Enable RDS2 symbol shifting" ON)
|
|
||||||
option(RDS2_DEBUG "Enable RDS2 debugging" OFF)
|
|
||||||
|
|
||||||
option(STATIC_LIBSAMPLERATE "Use a static libsamplerate library (.a)" OFF)
|
option(STATIC_LIBSAMPLERATE "Use a static libsamplerate library (.a)" OFF)
|
||||||
|
|
||||||
|
|
||||||
set(LIBSAMPLERATE_DIR "./libsamplerate" CACHE STRING "Directory for static libsamplerate")
|
set(LIBSAMPLERATE_DIR "./libsamplerate" CACHE STRING "Directory for static libsamplerate")
|
||||||
|
|
||||||
# Set compiler and flags
|
# Set compiler and flags
|
||||||
@@ -28,22 +22,6 @@ set(SOURCES
|
|||||||
rds.c
|
rds.c
|
||||||
)
|
)
|
||||||
|
|
||||||
# Handle RDS2 options
|
|
||||||
if(RDS2)
|
|
||||||
add_definitions(-DRDS2)
|
|
||||||
set(SOURCES ${SOURCES} rds2.c)
|
|
||||||
if(RDS2_QUADRATURE_CARRIER)
|
|
||||||
add_definitions(-DRDS2_QUADRATURE_CARRIER)
|
|
||||||
endif()
|
|
||||||
if(RDS2_SYMBOL_SHIFTING)
|
|
||||||
add_definitions(-DRDS2_SYMBOL_SHIFTING)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(RDS2_DEBUG)
|
|
||||||
add_definitions(-DRDS2_DEBUG)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(ODA_RTP)
|
if(ODA_RTP)
|
||||||
add_definitions(-DODA)
|
add_definitions(-DODA)
|
||||||
add_definitions(-DODA_RTP)
|
add_definitions(-DODA_RTP)
|
||||||
|
|||||||
@@ -56,18 +56,6 @@ void process_ascii_cmd(unsigned char *str) {
|
|||||||
arg = str + 4;
|
arg = str + 4;
|
||||||
|
|
||||||
if (CMD_MATCHES("MPX")) {
|
if (CMD_MATCHES("MPX")) {
|
||||||
#ifdef RDS2
|
|
||||||
float gains[5];
|
|
||||||
if (sscanf((char *)arg, "%f,%f,%f,%f,%f",
|
|
||||||
&gains[0], &gains[1], &gains[2], &gains[3],
|
|
||||||
&gains[4]) == 5) {
|
|
||||||
set_carrier_volume(0, gains[0]);
|
|
||||||
set_carrier_volume(1, gains[1]);
|
|
||||||
set_carrier_volume(2, gains[2]);
|
|
||||||
set_carrier_volume(3, gains[3]);
|
|
||||||
set_carrier_volume(4, gains[4]);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
float gains[2];
|
float gains[2];
|
||||||
if (sscanf((char *)arg, "%f,%f",
|
if (sscanf((char *)arg, "%f,%f",
|
||||||
&gains[0], &gains[1]) == 2)
|
&gains[0], &gains[1]) == 2)
|
||||||
@@ -75,7 +63,6 @@ void process_ascii_cmd(unsigned char *str) {
|
|||||||
set_carrier_volume(0, gains[0]);
|
set_carrier_volume(0, gains[0]);
|
||||||
set_carrier_volume(1, gains[1]);
|
set_carrier_volume(1, gains[1]);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (CMD_MATCHES("VOL")) {
|
if (CMD_MATCHES("VOL")) {
|
||||||
@@ -297,10 +284,7 @@ void process_ascii_cmd(unsigned char *str) {
|
|||||||
arg = str + 2;
|
arg = str + 2;
|
||||||
if (CMD_MATCHES("G")) {
|
if (CMD_MATCHES("G")) {
|
||||||
uint16_t blocks[4];
|
uint16_t blocks[4];
|
||||||
if(cmd_len == 18){
|
if(cmd_len == 14) {
|
||||||
/* RDS2 Group*/
|
|
||||||
/* do a ifdef rds2 here when implementing*/
|
|
||||||
} else if(cmd_len == 14) {
|
|
||||||
/* RDS1 Group*/
|
/* RDS1 Group*/
|
||||||
blocks[0] = get_rds_pi();
|
blocks[0] = get_rds_pi();
|
||||||
int count = sscanf((char *)arg, "%4hx%4hx%4hx", &blocks[1], &blocks[2], &blocks[3]);
|
int count = sscanf((char *)arg, "%4hx%4hx%4hx", &blocks[1], &blocks[2], &blocks[3]);
|
||||||
@@ -336,12 +320,7 @@ void process_ascii_cmd(unsigned char *str) {
|
|||||||
uint8_t val = strtoul((char *)arg, NULL, 10);
|
uint8_t val = strtoul((char *)arg, NULL, 10);
|
||||||
val /= 255;
|
val /= 255;
|
||||||
val *= 15; /* max value*/
|
val *= 15; /* max value*/
|
||||||
#ifdef RDS2
|
|
||||||
set_carrier_volume(1, val);
|
set_carrier_volume(1, val);
|
||||||
set_carrier_volume(2, val);
|
|
||||||
#else
|
|
||||||
set_carrier_volume(1, val);
|
|
||||||
#endif
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -378,12 +357,7 @@ void process_ascii_cmd(unsigned char *str) {
|
|||||||
uint8_t val = strtoul((char *)arg, NULL, 10);
|
uint8_t val = strtoul((char *)arg, NULL, 10);
|
||||||
val /= 255;
|
val /= 255;
|
||||||
val *= 15; /* max value*/
|
val *= 15; /* max value*/
|
||||||
#ifdef RDS2
|
|
||||||
set_carrier_volume(1, val);
|
set_carrier_volume(1, val);
|
||||||
set_carrier_volume(2, val);
|
|
||||||
#else
|
|
||||||
set_carrier_volume(1, val);
|
|
||||||
#endif
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
109
src/fm_mpx.c
109
src/fm_mpx.c
@@ -19,24 +19,9 @@
|
|||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
#include "rds.h"
|
#include "rds.h"
|
||||||
#ifdef RDS2
|
|
||||||
#include "rds2.h"
|
|
||||||
#endif
|
|
||||||
#include "fm_mpx.h"
|
#include "fm_mpx.h"
|
||||||
#include "osc.h"
|
#include "osc.h"
|
||||||
|
|
||||||
/*
|
|
||||||
* Local oscillator objects
|
|
||||||
* this is where the MPX waveforms are stored
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
static struct osc_t osc_57k;
|
|
||||||
#ifdef RDS2
|
|
||||||
static struct osc_t osc_67k;
|
|
||||||
static struct osc_t osc_71k;
|
|
||||||
static struct osc_t osc_76k;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static float mpx_vol;
|
static float mpx_vol;
|
||||||
|
|
||||||
static uint8_t rdsgen;
|
static uint8_t rdsgen;
|
||||||
@@ -47,46 +32,19 @@ void set_output_volume(float vol) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void set_rdsgen(uint8_t gen) {
|
void set_rdsgen(uint8_t gen) {
|
||||||
#ifdef RDS2
|
|
||||||
if (gen > 2) gen = 2;
|
|
||||||
#else
|
|
||||||
if (gen > 1) gen = 1;
|
if (gen > 1) gen = 1;
|
||||||
#endif
|
|
||||||
rdsgen = gen;
|
rdsgen = gen;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* subcarrier volumes */
|
|
||||||
static float volumes[MPX_SUBCARRIER_END] = {
|
|
||||||
0.0f, /* pilot tone: 0% */
|
|
||||||
1.0f, /* RDS: 7.5% modulation */
|
|
||||||
#ifdef RDS2
|
|
||||||
/* RDS2 */
|
|
||||||
0.05f, //66.5 khz 5%
|
|
||||||
0.05f, //71.25 khz 5%
|
|
||||||
0.0f //76 khz 0%
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
void set_carrier_volume(uint8_t carrier, float new_volume) {
|
void set_carrier_volume(uint8_t carrier, float new_volume) {
|
||||||
/* check for valid index */
|
/* check for valid index */
|
||||||
if (carrier >= MPX_SUBCARRIER_END) return;
|
if (carrier >= MPX_SUBCARRIER_END) return;
|
||||||
|
|
||||||
/* don't allow levels over 15% */
|
|
||||||
if (new_volume >= 15.0f) new_volume = 15.0f;
|
|
||||||
|
|
||||||
volumes[carrier] = new_volume / 100.0f;
|
volumes[carrier] = new_volume / 100.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
void fm_mpx_init(uint32_t sample_rate) {
|
void fm_mpx_init(uint32_t sample_rate) {
|
||||||
/* initialize the subcarrier oscillators */
|
|
||||||
osc_init(&osc_57k, sample_rate, 57000.0f);
|
|
||||||
rdsgen = 1;
|
rdsgen = 1;
|
||||||
#ifdef RDS2
|
|
||||||
osc_init(&osc_67k, sample_rate, 66500.0f);
|
|
||||||
osc_init(&osc_71k, sample_rate, 71250.0f);
|
|
||||||
osc_init(&osc_76k, sample_rate, 76000.0f);
|
|
||||||
rdsgen = 2;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void fm_rds_get_frames(float *outbuf, size_t num_frames) {
|
void fm_rds_get_frames(float *outbuf, size_t num_frames) {
|
||||||
@@ -96,68 +54,10 @@ void fm_rds_get_frames(float *outbuf, size_t num_frames) {
|
|||||||
for (size_t i = 0; i < num_frames; i++) {
|
for (size_t i = 0; i < num_frames; i++) {
|
||||||
out = 0.0f;
|
out = 0.0f;
|
||||||
|
|
||||||
#ifdef RDS2
|
|
||||||
out += osc_get_cos(&osc_57k)
|
|
||||||
* get_rds_sample(0, 0)
|
|
||||||
* volumes[MPX_SUBCARRIER_RDS_STREAM_0];
|
|
||||||
#else
|
|
||||||
out += get_rds_sample(0)
|
out += get_rds_sample(0)
|
||||||
* volumes[MPX_SUBCARRIER_RDS_STREAM_0];
|
* volumes[MPX_SUBCARRIER_RDS_STREAM_0];
|
||||||
#endif
|
|
||||||
#ifdef RDS2
|
|
||||||
#ifdef RDS2_QUADRATURE_CARRIER
|
|
||||||
/* RDS2 is quadrature phase */
|
|
||||||
|
|
||||||
/* 90 degree shift */
|
/* clipper */
|
||||||
if(rdsgen == 2 && volumes[MPX_SUBCARRIER_RDS2_STREAM_1] != 0) {
|
|
||||||
out += osc_get_sin(&osc_67k)
|
|
||||||
* get_rds_sample(1, 0)
|
|
||||||
* volumes[MPX_SUBCARRIER_RDS2_STREAM_1];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 180 degree shift */
|
|
||||||
if(rdsgen == 2 && volumes[MPX_SUBCARRIER_RDS2_STREAM_2] != 0) {
|
|
||||||
out += -osc_get_cos(&osc_71k)
|
|
||||||
* get_rds_sample(2, 0)
|
|
||||||
* volumes[MPX_SUBCARRIER_RDS2_STREAM_2];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 270 degree shift */
|
|
||||||
if(rdsgen == 2 && volumes[MPX_SUBCARRIER_RDS2_STREAM_3] != 0) {
|
|
||||||
out += -osc_get_sin(&osc_76k)
|
|
||||||
* get_rds_sample(3, 0)
|
|
||||||
* volumes[MPX_SUBCARRIER_RDS2_STREAM_3];
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
if(rdsgen == 2 && volumes[MPX_SUBCARRIER_RDS2_STREAM_1] != 0) {
|
|
||||||
out += osc_get_cos(&osc_67k)
|
|
||||||
* get_rds_sample(1, 0)
|
|
||||||
* volumes[MPX_SUBCARRIER_RDS2_STREAM_1];
|
|
||||||
}
|
|
||||||
|
|
||||||
if(rdsgen == 2 && volumes[MPX_SUBCARRIER_RDS2_STREAM_1] != 0) {
|
|
||||||
out += osc_get_cos(&osc_71k)
|
|
||||||
* get_rds_sample(2, 0)
|
|
||||||
* volumes[MPX_SUBCARRIER_RDS2_STREAM_2];
|
|
||||||
}
|
|
||||||
|
|
||||||
if(rdsgen == 2 && volumes[MPX_SUBCARRIER_RDS2_STREAM_3] != 0) {
|
|
||||||
out += osc_get_cos(&osc_76k)
|
|
||||||
* get_rds_sample(3, 0)
|
|
||||||
* volumes[MPX_SUBCARRIER_RDS2_STREAM_3];
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* update oscillator */
|
|
||||||
osc_update_pos(&osc_57k);
|
|
||||||
#ifdef RDS2
|
|
||||||
osc_update_pos(&osc_67k);
|
|
||||||
osc_update_pos(&osc_71k);
|
|
||||||
osc_update_pos(&osc_76k);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* declipper */
|
|
||||||
out = fminf(+1.0f, out);
|
out = fminf(+1.0f, out);
|
||||||
out = fmaxf(-1.0f, out);
|
out = fmaxf(-1.0f, out);
|
||||||
|
|
||||||
@@ -169,10 +69,5 @@ void fm_rds_get_frames(float *outbuf, size_t num_frames) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void fm_mpx_exit() {
|
void fm_mpx_exit() {
|
||||||
osc_exit(&osc_57k);
|
return;
|
||||||
#ifdef RDS2
|
|
||||||
osc_exit(&osc_67k);
|
|
||||||
osc_exit(&osc_71k);
|
|
||||||
osc_exit(&osc_76k);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|||||||
11
src/fm_mpx.h
11
src/fm_mpx.h
@@ -26,17 +26,6 @@
|
|||||||
|
|
||||||
#define OUTPUT_SAMPLE_RATE 192000
|
#define OUTPUT_SAMPLE_RATE 192000
|
||||||
|
|
||||||
enum mpx_subcarriers {
|
|
||||||
MPX_SUBCARRIER_ST_PILOT,
|
|
||||||
MPX_SUBCARRIER_RDS_STREAM_0,
|
|
||||||
#ifdef RDS2
|
|
||||||
MPX_SUBCARRIER_RDS2_STREAM_1,
|
|
||||||
MPX_SUBCARRIER_RDS2_STREAM_2,
|
|
||||||
MPX_SUBCARRIER_RDS2_STREAM_3,
|
|
||||||
#endif
|
|
||||||
MPX_SUBCARRIER_END
|
|
||||||
};
|
|
||||||
|
|
||||||
extern void fm_mpx_init(uint32_t 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_rds_get_frames(float *outbuf, size_t num_frames);
|
||||||
extern void fm_mpx_exit();
|
extern void fm_mpx_exit();
|
||||||
|
|||||||
30
src/lib.c
30
src/lib.c
@@ -21,7 +21,7 @@
|
|||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Stuff common for both RDS and RDS2
|
* Stuff for RDS
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -175,44 +175,16 @@ uint16_t crc16(uint8_t *data, size_t len) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Calculate the checkword for each block and emit the bits */
|
/* Calculate the checkword for each block and emit the bits */
|
||||||
#ifdef RDS2
|
|
||||||
void add_checkwords(uint16_t *blocks, uint8_t *bits, bool rds2)
|
|
||||||
#else
|
|
||||||
void add_checkwords(uint16_t *blocks, uint8_t *bits)
|
void add_checkwords(uint16_t *blocks, uint8_t *bits)
|
||||||
#endif
|
|
||||||
{
|
{
|
||||||
size_t i, j;
|
size_t i, j;
|
||||||
uint8_t bit, msb;
|
uint8_t bit, msb;
|
||||||
uint16_t block, block_crc, check, offset_word;
|
uint16_t block, block_crc, check, offset_word;
|
||||||
bool group_type_b = false;
|
bool group_type_b = false;
|
||||||
#ifdef RDS2
|
|
||||||
bool tunneling_type_b = false;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* if b11 is 1, then type B */
|
|
||||||
#ifdef RDS2
|
|
||||||
if (!rds2 && IS_TYPE_B(blocks))
|
|
||||||
#else
|
|
||||||
if (IS_TYPE_B(blocks))
|
if (IS_TYPE_B(blocks))
|
||||||
#endif
|
|
||||||
group_type_b = true;
|
group_type_b = true;
|
||||||
|
|
||||||
#ifdef RDS2
|
|
||||||
/* for when version B groups are coded in RDS2 */
|
|
||||||
if (rds2 && IS_TUNNELING(blocks) && IS_TYPE_B(blocks))
|
|
||||||
tunneling_type_b = true;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
for (i = 0; i < GROUP_LENGTH; i++) {
|
for (i = 0; i < GROUP_LENGTH; i++) {
|
||||||
#ifdef RDS2
|
|
||||||
/*
|
|
||||||
* If tunneling type B groups use offset
|
|
||||||
* word C' for block 3
|
|
||||||
*/
|
|
||||||
if (rds2 && i == 2 && tunneling_type_b) {
|
|
||||||
offset_word = offset_words[4];
|
|
||||||
} else
|
|
||||||
#endif
|
|
||||||
/* Group version B needs C' for block 3 */
|
/* Group version B needs C' for block 3 */
|
||||||
if (i == 2 && group_type_b) {
|
if (i == 2 && group_type_b) {
|
||||||
offset_word = offset_words[4];
|
offset_word = offset_words[4];
|
||||||
|
|||||||
@@ -23,11 +23,7 @@ extern int ustrcmp(const unsigned char *s1, const unsigned char *s2);
|
|||||||
|
|
||||||
extern uint8_t get_rtp_tag_id(char *rtp_tag_name);
|
extern uint8_t get_rtp_tag_id(char *rtp_tag_name);
|
||||||
extern char *get_rtp_tag_name(uint8_t rtp_tag);
|
extern char *get_rtp_tag_name(uint8_t rtp_tag);
|
||||||
#ifdef RDS2
|
|
||||||
extern void add_checkwords(uint16_t *blocks, uint8_t *bits, bool rds2);
|
|
||||||
#else
|
|
||||||
extern void add_checkwords(uint16_t *blocks, uint8_t *bits);
|
extern void add_checkwords(uint16_t *blocks, uint8_t *bits);
|
||||||
#endif
|
|
||||||
extern uint8_t add_rds_af(struct rds_af_t *af_list, float freq);
|
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 char *show_af_list(struct rds_af_t af_list);
|
||||||
extern uint16_t crc16(uint8_t *data, size_t len);
|
extern uint16_t crc16(uint8_t *data, size_t len);
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Create station logo data to build into the mpxgen executable for RDS2
|
|
||||||
|
|
||||||
if [ -z "$1" ]; then
|
|
||||||
echo "Usage: $0 <logo file>"
|
|
||||||
exit
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -f "$1" ]; then
|
|
||||||
echo 'static const unsigned char station_logo[] = {' > rds2_image_data.c
|
|
||||||
xxd -i < "$1" >> rds2_image_data.c
|
|
||||||
echo '};' >> rds2_image_data.c
|
|
||||||
echo 'static const unsigned int station_logo_len = sizeof(station_logo);' >> rds2_image_data.c
|
|
||||||
fi
|
|
||||||
@@ -68,7 +68,7 @@ static void show_help(char *name) {
|
|||||||
"Usage: %s [options]\n"
|
"Usage: %s [options]\n"
|
||||||
"\n"
|
"\n"
|
||||||
" -i,--pi Program Identification code\n"
|
" -i,--pi Program Identification code\n"
|
||||||
" [default: 3073]\n"
|
" [default: 305F]\n"
|
||||||
" -s,--ps Program Service name\n"
|
" -s,--ps Program Service name\n"
|
||||||
" [default: \"radio95\"]\n"
|
" [default: \"radio95\"]\n"
|
||||||
" -r,--rt1 Radio Text 1\n"
|
" -r,--rt1 Radio Text 1\n"
|
||||||
@@ -84,9 +84,6 @@ static void show_help(char *name) {
|
|||||||
" -e,--ecc ECC code\n"
|
" -e,--ecc ECC code\n"
|
||||||
" -d,--di DI code\n"
|
" -d,--di DI code\n"
|
||||||
" -C,--ctl FIFO control pipe\n"
|
" -C,--ctl FIFO control pipe\n"
|
||||||
#ifdef RDS2
|
|
||||||
" -I,--img RDS2 Logo path\n"
|
|
||||||
#endif
|
|
||||||
" -h,--help Show this help text and exit\n"
|
" -h,--help Show this help text and exit\n"
|
||||||
" -v,--version Show version and exit\n"
|
" -v,--version Show version and exit\n"
|
||||||
"\n",
|
"\n",
|
||||||
@@ -134,9 +131,6 @@ int main(int argc, char **argv) {
|
|||||||
pthread_cond_t control_pipe_cond;
|
pthread_cond_t control_pipe_cond;
|
||||||
|
|
||||||
const char *short_opt = "R:i:s:r:p:T:A:P:l:e:L:d:C:"
|
const char *short_opt = "R:i:s:r:p:T:A:P:l:e:L:d:C:"
|
||||||
#ifdef RDS2
|
|
||||||
"I:"
|
|
||||||
#endif
|
|
||||||
"hv";
|
"hv";
|
||||||
|
|
||||||
struct option long_opt[] =
|
struct option long_opt[] =
|
||||||
@@ -154,9 +148,6 @@ int main(int argc, char **argv) {
|
|||||||
{"lic", required_argument, NULL, 'L'},
|
{"lic", required_argument, NULL, 'L'},
|
||||||
{"di", required_argument, NULL, 'd'},
|
{"di", required_argument, NULL, 'd'},
|
||||||
{"ctl", required_argument, NULL, 'C'},
|
{"ctl", required_argument, NULL, 'C'},
|
||||||
#ifdef RDS2
|
|
||||||
{"img", required_argument, NULL, 'I'},
|
|
||||||
#endif
|
|
||||||
|
|
||||||
{"help", no_argument, NULL, 'h'},
|
{"help", no_argument, NULL, 'h'},
|
||||||
{"version", no_argument, NULL, 'v'},
|
{"version", no_argument, NULL, 'v'},
|
||||||
@@ -215,12 +206,6 @@ keep_parsing_opts:
|
|||||||
memcpy(control_pipe, optarg, 50);
|
memcpy(control_pipe, optarg, 50);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
#ifdef RDS2
|
|
||||||
case 'I': /* img */
|
|
||||||
memcpy(rds_params.rds2_image_path, optarg, 50);
|
|
||||||
break;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
case 'v': /* version */
|
case 'v': /* version */
|
||||||
show_version();
|
show_version();
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -18,9 +18,6 @@
|
|||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "rds.h"
|
#include "rds.h"
|
||||||
#ifdef RDS2
|
|
||||||
#include "rds2.h"
|
|
||||||
#endif
|
|
||||||
#include "fm_mpx.h"
|
#include "fm_mpx.h"
|
||||||
#include "waveforms.h"
|
#include "waveforms.h"
|
||||||
#include "modulator.h"
|
#include "modulator.h"
|
||||||
@@ -33,56 +30,13 @@ static float **waveform;
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void init_rds_objects() {
|
void init_rds_objects() {
|
||||||
rds_ctx = malloc(NUM_STREAMS * sizeof(struct rds_t));
|
rds_ctx = malloc(sizeof(struct rds_t));
|
||||||
|
|
||||||
for (uint8_t i = 0; i < NUM_STREAMS; i++) {
|
rds_ctx[0] = malloc(sizeof(struct rds_t));
|
||||||
rds_ctx[i] = malloc(sizeof(struct rds_t));
|
rds_ctx[0]->bit_buffer = malloc(BITS_PER_GROUP);
|
||||||
rds_ctx[i]->bit_buffer = malloc(BITS_PER_GROUP);
|
rds_ctx[0]->sample_buffer =
|
||||||
rds_ctx[i]->sample_buffer =
|
malloc(SAMPLE_BUFFER_SIZE * sizeof(float));
|
||||||
malloc(SAMPLE_BUFFER_SIZE * sizeof(float));
|
rds_ctx[0]->symbol_shift_buf_idx = 0;
|
||||||
|
|
||||||
#ifdef RDS2_SYMBOL_SHIFTING
|
|
||||||
/*
|
|
||||||
* symbol shifting to reduce total power of aggregate carriers
|
|
||||||
*
|
|
||||||
* see:
|
|
||||||
* https://ietresearch.onlinelibrary.wiley.com/doi/pdf/10.1049/el.2019.0292
|
|
||||||
* for more information
|
|
||||||
*/
|
|
||||||
switch (i) {
|
|
||||||
case 1:
|
|
||||||
/* time offset of 1/2 */
|
|
||||||
rds_ctx[i]->symbol_shift = SAMPLES_PER_BIT / 2;
|
|
||||||
rds_ctx[i]->symbol_shift_buf = malloc(
|
|
||||||
rds_ctx[i]->symbol_shift * sizeof(float));
|
|
||||||
memset(rds_ctx[i]->symbol_shift_buf, 0,
|
|
||||||
rds_ctx[i]->symbol_shift * sizeof(float));
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
/* time offset of 1/4 */
|
|
||||||
rds_ctx[i]->symbol_shift = SAMPLES_PER_BIT / 4;
|
|
||||||
rds_ctx[i]->symbol_shift_buf = malloc(
|
|
||||||
rds_ctx[i]->symbol_shift * sizeof(float));
|
|
||||||
memset(rds_ctx[i]->symbol_shift_buf, 0,
|
|
||||||
rds_ctx[i]->symbol_shift * sizeof(float));
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
/* time offset of 3/4 */
|
|
||||||
rds_ctx[i]->symbol_shift = (SAMPLES_PER_BIT / 4) * 3;
|
|
||||||
rds_ctx[i]->symbol_shift_buf = malloc(
|
|
||||||
rds_ctx[i]->symbol_shift * sizeof(float));
|
|
||||||
memset(rds_ctx[i]->symbol_shift_buf, 0,
|
|
||||||
rds_ctx[i]->symbol_shift * sizeof(float));
|
|
||||||
break;
|
|
||||||
default: /* stream 0 */
|
|
||||||
/* no time offset */
|
|
||||||
rds_ctx[i]->symbol_shift = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
rds_ctx[i]->symbol_shift_buf_idx = 0;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
waveform = malloc(2 * sizeof(float));
|
waveform = malloc(2 * sizeof(float));
|
||||||
|
|
||||||
@@ -96,15 +50,13 @@ void init_rds_objects() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void exit_rds_objects() {
|
void exit_rds_objects() {
|
||||||
for (uint8_t i = 0; i < NUM_STREAMS; i++) {
|
int has_symbol_shift = rds_ctx[i]->symbol_shift;
|
||||||
int has_symbol_shift = rds_ctx[i]->symbol_shift;
|
if (has_symbol_shift) {
|
||||||
if (has_symbol_shift) {
|
free(rds_ctx[i]->symbol_shift_buf);
|
||||||
free(rds_ctx[i]->symbol_shift_buf);
|
}
|
||||||
}
|
free(rds_ctx[i]->sample_buffer);
|
||||||
free(rds_ctx[i]->sample_buffer);
|
free(rds_ctx[i]->bit_buffer);
|
||||||
free(rds_ctx[i]->bit_buffer);
|
free(rds_ctx[i]);
|
||||||
free(rds_ctx[i]);
|
|
||||||
}
|
|
||||||
free(rds_ctx);
|
free(rds_ctx);
|
||||||
|
|
||||||
for (uint8_t i = 0; i < 2; i++) {
|
for (uint8_t i = 0; i < 2; i++) {
|
||||||
@@ -117,11 +69,7 @@ void exit_rds_objects() {
|
|||||||
/* Get an RDS sample. This generates the envelope of the waveform using
|
/* Get an RDS sample. This generates the envelope of the waveform using
|
||||||
* pre-generated elementary waveform samples.
|
* pre-generated elementary waveform samples.
|
||||||
*/
|
*/
|
||||||
#ifdef RDS2
|
|
||||||
float get_rds_sample(uint8_t stream_num, uint8_t rds2tunneling) {
|
|
||||||
#else
|
|
||||||
float get_rds_sample(uint8_t stream_num) {
|
float get_rds_sample(uint8_t stream_num) {
|
||||||
#endif
|
|
||||||
struct rds_t *rds;
|
struct rds_t *rds;
|
||||||
uint16_t idx;
|
uint16_t idx;
|
||||||
float *cur_waveform;
|
float *cur_waveform;
|
||||||
@@ -132,19 +80,7 @@ float get_rds_sample(uint8_t stream_num) {
|
|||||||
|
|
||||||
if (rds->sample_count == SAMPLES_PER_BIT) {
|
if (rds->sample_count == SAMPLES_PER_BIT) {
|
||||||
if (rds->bit_pos == BITS_PER_GROUP) {
|
if (rds->bit_pos == BITS_PER_GROUP) {
|
||||||
#ifdef RDS2
|
|
||||||
if(!rds2tunneling) {
|
|
||||||
if (stream_num > 0) {
|
|
||||||
get_rds2_bits(stream_num, rds->bit_buffer);
|
|
||||||
} else {
|
|
||||||
get_rds_bits(rds->bit_buffer);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
get_rds_bits(rds->bit_buffer);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
get_rds_bits(rds->bit_buffer);
|
get_rds_bits(rds->bit_buffer);
|
||||||
#endif
|
|
||||||
rds->bit_pos = 0;
|
rds->bit_pos = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,12 +16,6 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifdef RDS2
|
|
||||||
#define NUM_STREAMS 4
|
|
||||||
#else
|
|
||||||
#define NUM_STREAMS 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* RDS signal context */
|
/* RDS signal context */
|
||||||
typedef struct rds_t {
|
typedef struct rds_t {
|
||||||
uint8_t *bit_buffer; /* BITS_PER_GROUP */
|
uint8_t *bit_buffer; /* BITS_PER_GROUP */
|
||||||
|
|||||||
108
src/osc.c
108
src/osc.c
@@ -1,108 +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 <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "common.h"
|
|
||||||
#include "osc.h"
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Code for MPX oscillator
|
|
||||||
*
|
|
||||||
* This uses lookup tables to speed up the waveform generation
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* DDS function generator
|
|
||||||
*
|
|
||||||
* Create wave constants for a given frequency
|
|
||||||
*/
|
|
||||||
static void create_wave(uint32_t rate, float freq,
|
|
||||||
float *sin_wave, float *cos_wave, uint16_t *max_phase) {
|
|
||||||
double sin_sample, cos_sample;
|
|
||||||
/* used to determine if we have completed a cycle */
|
|
||||||
uint16_t periods = round((double)rate/freq);
|
|
||||||
const double w = M_2PI * freq / rate;
|
|
||||||
|
|
||||||
/* First value of a sine wave is always 0 */
|
|
||||||
*sin_wave++ = 0.0f;
|
|
||||||
*cos_wave++ = 1.0f;
|
|
||||||
|
|
||||||
for(uint16_t i = 1; i < periods; i++) {
|
|
||||||
sin_sample = sin(w * i);
|
|
||||||
cos_sample = cos(w * i);
|
|
||||||
*sin_wave++ = (float)sin_sample;
|
|
||||||
*cos_wave++ = (float)cos_sample;
|
|
||||||
}
|
|
||||||
|
|
||||||
*max_phase = periods;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Oscillator object initialization
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
void osc_init(struct osc_t *osc, uint32_t sample_rate, float freq) {
|
|
||||||
|
|
||||||
/* sample rate for the objects */
|
|
||||||
osc->sample_rate = sample_rate;
|
|
||||||
|
|
||||||
/* waveform tables */
|
|
||||||
osc->sin_wave = malloc(osc->sample_rate * sizeof(float));
|
|
||||||
osc->cos_wave = malloc(osc->sample_rate * sizeof(float));
|
|
||||||
|
|
||||||
/* set current position to 0 */
|
|
||||||
osc->cur = 0;
|
|
||||||
|
|
||||||
/* create waveform data and load into lookup tables */
|
|
||||||
create_wave(osc->sample_rate, freq,
|
|
||||||
osc->sin_wave, osc->cos_wave,
|
|
||||||
&osc->max);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Get a single waveform sample
|
|
||||||
*
|
|
||||||
* Cosine is needed for SSB generation
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
float osc_get_cos(struct osc_t *osc) {
|
|
||||||
return osc->cos_wave[osc->cur];
|
|
||||||
}
|
|
||||||
|
|
||||||
float osc_get_sin(struct osc_t *osc) {
|
|
||||||
return osc->sin_wave[osc->cur];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Shift the oscillator to the next position
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
void osc_update_pos(struct osc_t *osc) {
|
|
||||||
if (++osc->cur == osc->max) osc->cur = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Unload waveform tables
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
void osc_exit(struct osc_t *osc) {
|
|
||||||
free(osc->sin_wave);
|
|
||||||
free(osc->cos_wave);
|
|
||||||
osc->cur = 0;
|
|
||||||
osc->max = 0;
|
|
||||||
}
|
|
||||||
50
src/osc.h
50
src/osc.h
@@ -1,50 +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 <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* context for MPX oscillator */
|
|
||||||
typedef struct osc_t {
|
|
||||||
/* the sample rate at which the oscillator operates */
|
|
||||||
uint32_t sample_rate;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* frequency for this instance
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
float freq;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Arrays of carrier wave constants
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
float *sin_wave_out_of_phase;
|
|
||||||
float *sin_wave;
|
|
||||||
float *cos_wave;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Wave phase
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
uint16_t cur;
|
|
||||||
uint16_t max;
|
|
||||||
} osc_t;
|
|
||||||
|
|
||||||
extern void osc_init(struct osc_t *osc, uint32_t sample_rate, const float freq);
|
|
||||||
extern float osc_get_sin(struct osc_t *osc);
|
|
||||||
extern float osc_get_cos(struct osc_t *osc);
|
|
||||||
extern void osc_update_pos(struct osc_t *osc);
|
|
||||||
extern void osc_exit(struct osc_t *osc);
|
|
||||||
13
src/rds.c
13
src/rds.c
@@ -19,9 +19,6 @@
|
|||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "rds.h"
|
#include "rds.h"
|
||||||
#include "modulator.h"
|
#include "modulator.h"
|
||||||
#ifdef RDS2
|
|
||||||
#include "rds2.h"
|
|
||||||
#endif
|
|
||||||
#include "lib.h"
|
#include "lib.h"
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
@@ -529,11 +526,7 @@ group_coded:
|
|||||||
void get_rds_bits(uint8_t *bits) {
|
void get_rds_bits(uint8_t *bits) {
|
||||||
static uint16_t out_blocks[GROUP_LENGTH];
|
static uint16_t out_blocks[GROUP_LENGTH];
|
||||||
get_rds_group(out_blocks);
|
get_rds_group(out_blocks);
|
||||||
#ifdef RDS2
|
|
||||||
add_checkwords(out_blocks, bits, false);
|
|
||||||
#else
|
|
||||||
add_checkwords(out_blocks, bits);
|
add_checkwords(out_blocks, bits);
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void init_rds_encoder(struct rds_params_t rds_params) {
|
void init_rds_encoder(struct rds_params_t rds_params) {
|
||||||
@@ -570,16 +563,10 @@ void init_rds_encoder(struct rds_params_t rds_params) {
|
|||||||
|
|
||||||
/* initialize modulator objects */
|
/* initialize modulator objects */
|
||||||
init_rds_objects();
|
init_rds_objects();
|
||||||
#ifdef RDS2
|
|
||||||
init_rds2_encoder(rds_params.rds2_image_path);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void exit_rds_encoder() {
|
void exit_rds_encoder() {
|
||||||
exit_rds_objects();
|
exit_rds_objects();
|
||||||
#ifdef RDS2
|
|
||||||
exit_rds2_encoder();
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_rds_pi(uint16_t pi_code) {
|
void set_rds_pi(uint16_t pi_code) {
|
||||||
|
|||||||
19
src/rds.h
19
src/rds.h
@@ -93,11 +93,6 @@ typedef struct rds_params_t {
|
|||||||
uint8_t pin_day;
|
uint8_t pin_day;
|
||||||
uint8_t pin_hour;
|
uint8_t pin_hour;
|
||||||
uint8_t pin_minute;
|
uint8_t pin_minute;
|
||||||
|
|
||||||
#ifdef RDS2
|
|
||||||
/* RDS2 image path*/
|
|
||||||
char rds2_image_path[51];
|
|
||||||
#endif
|
|
||||||
} rds_params_t;
|
} rds_params_t;
|
||||||
/* Here, the first member of the struct must be a scalar to avoid a
|
/* Here, the first member of the struct must be a scalar to avoid a
|
||||||
warning on -Wmissing-braces with GCC < 4.8.3
|
warning on -Wmissing-braces with GCC < 4.8.3
|
||||||
@@ -259,14 +254,7 @@ typedef struct rds_params_t {
|
|||||||
#define INT16_14 0x4000
|
#define INT16_14 0x4000
|
||||||
#define INT16_15 0x8000
|
#define INT16_15 0x8000
|
||||||
|
|
||||||
/* 18 bit (for RDS2) */
|
|
||||||
#define INT18_U2 0x30000
|
|
||||||
#define INT18_L4 0x0000f
|
|
||||||
|
|
||||||
#define IS_TYPE_B(a) (a[1] & INT16_11)
|
#define IS_TYPE_B(a) (a[1] & INT16_11)
|
||||||
#ifdef RDS2
|
|
||||||
#define IS_TUNNELING(a) (a[0] == 0x0000)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef ODA
|
#ifdef ODA
|
||||||
/*
|
/*
|
||||||
@@ -291,9 +279,6 @@ typedef struct rds_oda_t {
|
|||||||
*/
|
*/
|
||||||
#define ODA_AID_RTPLUS 0x4bd7
|
#define ODA_AID_RTPLUS 0x4bd7
|
||||||
#endif
|
#endif
|
||||||
/* RDS2 */
|
|
||||||
#define ODA_AID_RFT 0xff7f
|
|
||||||
#define ODA_AID_RFTPLUS 0xff80
|
|
||||||
|
|
||||||
extern void init_rds_encoder(struct rds_params_t rds_params);
|
extern void init_rds_encoder(struct rds_params_t rds_params);
|
||||||
extern void exit_rds_encoder();
|
extern void exit_rds_encoder();
|
||||||
@@ -325,11 +310,7 @@ extern void set_rds_tp(uint8_t tp);
|
|||||||
extern void set_rds_ms(uint8_t ms);
|
extern void set_rds_ms(uint8_t ms);
|
||||||
extern void set_rds_ct(uint8_t ct);
|
extern void set_rds_ct(uint8_t ct);
|
||||||
extern void set_rds_di(uint8_t di);
|
extern void set_rds_di(uint8_t di);
|
||||||
#ifdef RDS2
|
|
||||||
extern float get_rds_sample(uint8_t stream_num, uint8_t rds2tunneling);
|
|
||||||
#else
|
|
||||||
extern float get_rds_sample(uint8_t stream_num);
|
extern float get_rds_sample(uint8_t stream_num);
|
||||||
#endif
|
|
||||||
extern uint16_t get_rds_pi();
|
extern uint16_t get_rds_pi();
|
||||||
extern void set_rds_cg(uint16_t* blocks);
|
extern void set_rds_cg(uint16_t* blocks);
|
||||||
|
|
||||||
|
|||||||
452
src/rds2.c
452
src/rds2.c
@@ -1,452 +0,0 @@
|
|||||||
/*
|
|
||||||
* mpxgen - FM multiplex encoder with Stereo and RDS
|
|
||||||
* Copyright (C) 2019-2020 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 <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "common.h"
|
|
||||||
#include "rds.h"
|
|
||||||
#include "rds2.h"
|
|
||||||
#include "lib.h"
|
|
||||||
|
|
||||||
/*
|
|
||||||
* RDS2-specific stuff
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
/* RFT */
|
|
||||||
|
|
||||||
/* fallback station logo */
|
|
||||||
#include "rds2_image_data.c"
|
|
||||||
|
|
||||||
/* station logo */
|
|
||||||
static struct rft_t station_logo_stream;
|
|
||||||
|
|
||||||
static void init_rft(struct rft_t *rft, uint8_t file_id,
|
|
||||||
bool usecrc, uint8_t crc_mode, char *file_path) {
|
|
||||||
uint32_t seg_counter = 0;
|
|
||||||
uint32_t crc_chunk_counter = 0;
|
|
||||||
uint16_t crc_chunk_size = 0;
|
|
||||||
FILE *image_file_hdlr;
|
|
||||||
|
|
||||||
rft->file_path = malloc(MAX_FILE_NAME_LEN + 1);
|
|
||||||
rft->file_path[MAX_FILE_NAME_LEN] = 0;
|
|
||||||
rft->file_data = malloc(MAX_IMAGE_LEN);
|
|
||||||
rft->crcs = malloc(MAX_CRC_CHUNK_ADDR * sizeof(uint16_t));
|
|
||||||
|
|
||||||
/* unused data must be padded with zeroes */
|
|
||||||
memset(rft->file_data, 0, MAX_IMAGE_LEN);
|
|
||||||
|
|
||||||
strncpy(rft->file_path, file_path, MAX_FILE_NAME_LEN);
|
|
||||||
|
|
||||||
/* open file in binary mode */
|
|
||||||
image_file_hdlr = fopen(file_path, "rb");
|
|
||||||
if (image_file_hdlr != NULL) {
|
|
||||||
fseek(image_file_hdlr, 0, SEEK_END);
|
|
||||||
rft->file_len = ftell(image_file_hdlr);
|
|
||||||
rewind(image_file_hdlr);
|
|
||||||
|
|
||||||
/* image cannot be larger than 163840 bytes */
|
|
||||||
if (rft->file_len > MAX_IMAGE_LEN) {
|
|
||||||
#ifdef RDS2_DEBUG
|
|
||||||
fprintf(stderr, "W: Image too large!\n");
|
|
||||||
#endif
|
|
||||||
rft->file_len = MAX_IMAGE_LEN;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* copy image data to local buffer */
|
|
||||||
fread(rft->file_data, rft->file_len, 1, image_file_hdlr);
|
|
||||||
|
|
||||||
fclose(image_file_hdlr);
|
|
||||||
} else { /* fallback in case file can't be read at startup */
|
|
||||||
rft->file_len = station_logo_len;
|
|
||||||
|
|
||||||
if (rft->file_len > MAX_IMAGE_LEN) {
|
|
||||||
rft->file_len = MAX_IMAGE_LEN;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(rft->file_data, station_logo, rft->file_len);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* keep track of file lengths so we know when to toggle */
|
|
||||||
rft->prev_file_len = rft->file_len;
|
|
||||||
rft->channel = 0;
|
|
||||||
rft->file_id = file_id;
|
|
||||||
rft->toggle = 0;
|
|
||||||
rft->num_segs = 0;
|
|
||||||
|
|
||||||
/* determine how many segments we need */
|
|
||||||
while (seg_counter < rft->file_len) {
|
|
||||||
seg_counter += 5;
|
|
||||||
rft->num_segs++;
|
|
||||||
}
|
|
||||||
|
|
||||||
rft->use_crc = usecrc;
|
|
||||||
|
|
||||||
/* if CRC is disabled exit early */
|
|
||||||
if (!rft->use_crc) {
|
|
||||||
rft->crc_mode = 0;
|
|
||||||
rft->num_crc_chunks = 1; /* only 1 */
|
|
||||||
rft->crcs[0] = 0x0000;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
rft->crc_mode = crc_mode & 7;
|
|
||||||
|
|
||||||
switch (rft->crc_mode) {
|
|
||||||
case RFT_CRC_MODE_ENTIRE_FILE:
|
|
||||||
/* no-op */
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RFT_CRC_MODE_16_GROUPS:
|
|
||||||
crc_chunk_size = 16;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RFT_CRC_MODE_32_GROUPS:
|
|
||||||
crc_chunk_size = 32;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RFT_CRC_MODE_64_GROUPS:
|
|
||||||
crc_chunk_size = 64;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RFT_CRC_MODE_128_GROUPS:
|
|
||||||
crc_chunk_size = 128;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RFT_CRC_MODE_256_GROUPS:
|
|
||||||
crc_chunk_size = 256;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RFT_CRC_MODE_RFU: /* reserved - use auto mode */
|
|
||||||
case RFT_CRC_MODE_AUTO:
|
|
||||||
if (rft->file_len >= 81920) {
|
|
||||||
crc_chunk_size = 64;
|
|
||||||
rft->crc_mode = RFT_CRC_MODE_64_GROUPS;
|
|
||||||
} else if (rft->file_len > 40960) {
|
|
||||||
crc_chunk_size = 32;
|
|
||||||
rft->crc_mode = RFT_CRC_MODE_32_GROUPS;
|
|
||||||
} else {
|
|
||||||
crc_chunk_size = 16;
|
|
||||||
rft->crc_mode = RFT_CRC_MODE_16_GROUPS;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rft->crc_mode) { /* chunked modes */
|
|
||||||
/* RFT packet size = chunk size * 5 data bytes per group */
|
|
||||||
rft->pkt_size = crc_chunk_size * 5;
|
|
||||||
|
|
||||||
/* the remainder (if file size is not a multiple of pkt_size) */
|
|
||||||
rft->pkt_size_rem = rft->file_len % rft->pkt_size;
|
|
||||||
|
|
||||||
/* calculate number of CRC chunks */
|
|
||||||
while (crc_chunk_counter <= rft->file_len) {
|
|
||||||
crc_chunk_counter += rft->pkt_size;
|
|
||||||
rft->num_crc_chunks++;
|
|
||||||
}
|
|
||||||
|
|
||||||
rft->crc_chunks = malloc(rft->pkt_size);
|
|
||||||
|
|
||||||
/* calculate CRC's for chunks */
|
|
||||||
for (uint16_t i = 0; i < rft->num_crc_chunks; i++) {
|
|
||||||
memset(rft->crc_chunks, 0, rft->pkt_size);
|
|
||||||
memcpy(rft->crc_chunks,
|
|
||||||
rft->file_data + i * rft->pkt_size, rft->pkt_size);
|
|
||||||
if ((i == rft->num_crc_chunks - 1) && rft->pkt_size_rem) {
|
|
||||||
/* last chunk may have less bytes */
|
|
||||||
rft->crcs[i] = crc16(rft->crc_chunks, rft->pkt_size_rem);
|
|
||||||
} else {
|
|
||||||
rft->crcs[i] = crc16(rft->crc_chunks, rft->pkt_size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
free(rft->crc_chunks);
|
|
||||||
} else { /* CRC of entire file */
|
|
||||||
rft->num_crc_chunks = 1; /* only 1 */
|
|
||||||
rft->crcs[0] = crc16(rft->file_data, rft->file_len);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void update_rft(struct rft_t *rft) {
|
|
||||||
uint32_t seg_counter = 0;
|
|
||||||
FILE *image_file_hdlr;
|
|
||||||
|
|
||||||
/* open file in binary mode */
|
|
||||||
image_file_hdlr = fopen(rft->file_path, "rb");
|
|
||||||
if (image_file_hdlr == NULL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
rft->prev_file_len = rft->file_len;
|
|
||||||
|
|
||||||
/* update size field */
|
|
||||||
fseek(image_file_hdlr, 0, SEEK_END);
|
|
||||||
rft->file_len = ftell(image_file_hdlr);
|
|
||||||
rewind(image_file_hdlr);
|
|
||||||
|
|
||||||
/* file didn't change so return early */
|
|
||||||
if (rft->file_len == rft->prev_file_len) {
|
|
||||||
fclose(image_file_hdlr);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* image cannot be larger than 163840 bytes */
|
|
||||||
if (rft->file_len > MAX_IMAGE_LEN) {
|
|
||||||
#ifdef RDS2_DEBUG
|
|
||||||
fprintf(stderr, "W: Image too large!\n");
|
|
||||||
#endif
|
|
||||||
rft->file_len = MAX_IMAGE_LEN;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* copy image data to local buffer */
|
|
||||||
fread(rft->file_data, rft->file_len, 1, image_file_hdlr);
|
|
||||||
|
|
||||||
fclose(image_file_hdlr);
|
|
||||||
|
|
||||||
rft->num_segs = 0;
|
|
||||||
|
|
||||||
/* recalculate # of needed segments */
|
|
||||||
while (seg_counter < rft->file_len) {
|
|
||||||
seg_counter += 5;
|
|
||||||
rft->num_segs++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rft->crc_mode) {
|
|
||||||
/* recalculate CRC's for chunks */
|
|
||||||
for (uint16_t i = 0; i < rft->num_crc_chunks; i++) {
|
|
||||||
memset(rft->crc_chunks, 0, rft->pkt_size);
|
|
||||||
memcpy(rft->crc_chunks,
|
|
||||||
rft->file_data + i * rft->pkt_size, rft->pkt_size);
|
|
||||||
if ((i == rft->num_crc_chunks - 1) && rft->pkt_size_rem) {
|
|
||||||
/* last chunk may have less bytes */
|
|
||||||
rft->crcs[i] = crc16(rft->crc_chunks, rft->pkt_size_rem);
|
|
||||||
} else {
|
|
||||||
rft->crcs[i] = crc16(rft->crc_chunks, rft->pkt_size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
free(rft->crc_chunks);
|
|
||||||
} else { /* CRC of entire file */
|
|
||||||
rft->num_crc_chunks = 1; /* only 1 */
|
|
||||||
rft->crcs[0] = crc16(rft->file_data, rft->file_len);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (++rft->file_version == 8) {
|
|
||||||
rft->file_version = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void exit_rft(struct rft_t *rft) {
|
|
||||||
free(rft->file_data);
|
|
||||||
free(rft->file_path);
|
|
||||||
free(rft->crcs);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* RFT variant 0 (file metadata)
|
|
||||||
*
|
|
||||||
* Carries:
|
|
||||||
* - Function Header
|
|
||||||
* - Pipe Number
|
|
||||||
* - ODA ID
|
|
||||||
* - Variant code (0)
|
|
||||||
* - CRC-16 Flag
|
|
||||||
* - File ID
|
|
||||||
* - File size (18 bits)
|
|
||||||
*/
|
|
||||||
static void get_rft_var_0_data_group(struct rft_t *rft, uint16_t *blocks) {
|
|
||||||
/* function header */
|
|
||||||
blocks[0] = 1 << 15;
|
|
||||||
blocks[0] |= (rft->channel & INT8_L4); /* pipe number */
|
|
||||||
|
|
||||||
/* ODA ID */
|
|
||||||
blocks[1] = ODA_AID_RFT;
|
|
||||||
|
|
||||||
blocks[2] = (0 & INT18_L4) << 12; /* variant code */
|
|
||||||
blocks[2] |= ((rft->use_crc ? 1 : 0) & INT8_L1) << 11; /* CRC */
|
|
||||||
blocks[2] |= (rft->file_version & INT8_L3) << 8; /* file version */
|
|
||||||
blocks[2] |= (rft->file_id & INT8_L6) << 2; /* file ID */
|
|
||||||
blocks[2] |= (rft->file_len & INT18_U2) >> 16;
|
|
||||||
|
|
||||||
blocks[3] = rft->file_len & INT16_ALL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* RFT variant 1 (chunk to CRC mappings)
|
|
||||||
*
|
|
||||||
* Carries:
|
|
||||||
* - Function Header
|
|
||||||
* - Pipe Number
|
|
||||||
* - ODA ID
|
|
||||||
* - Variant Code (1)
|
|
||||||
* - CRC Mode
|
|
||||||
* - Chunk Address (9 bits)
|
|
||||||
* - CRC
|
|
||||||
*/
|
|
||||||
static void get_rft_var_1_data_group(struct rft_t *rft, uint16_t *blocks) {
|
|
||||||
/* function header */
|
|
||||||
blocks[0] = 1 << 15;
|
|
||||||
blocks[0] |= (rft->channel & INT8_L4); /* pipe number */
|
|
||||||
|
|
||||||
/* ODA ID */
|
|
||||||
blocks[1] = ODA_AID_RFT;
|
|
||||||
|
|
||||||
blocks[2] = (1 & INT8_L4) << 12; /* variant code */
|
|
||||||
blocks[2] |= (rft->crc_mode & INT8_L3) << 9; /* CRC mode */
|
|
||||||
/* chunk address */
|
|
||||||
blocks[2] |= rft->crc_chunk_addr & INT16_L9;
|
|
||||||
|
|
||||||
/* CRC */
|
|
||||||
blocks[3] = rft->crcs[rft->crc_chunk_addr];
|
|
||||||
|
|
||||||
if (++rft->crc_chunk_addr > rft->num_crc_chunks) {
|
|
||||||
#ifdef RDS2_DEBUG
|
|
||||||
fprintf(stderr, "File CRC sending complete\n");
|
|
||||||
#endif
|
|
||||||
rft->crc_chunk_addr = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* RFT variants 2-15 (custom variants)
|
|
||||||
*
|
|
||||||
* Carries:
|
|
||||||
* - Function Header
|
|
||||||
* - Pipe Number
|
|
||||||
* - Variant code
|
|
||||||
* - Variant data:
|
|
||||||
* - 2-7 carries file-related data
|
|
||||||
* - 8-15 ODA data that is not related
|
|
||||||
*/
|
|
||||||
#if 0
|
|
||||||
static void get_rft_var_2_15_data_group(struct rft_t *rft, uint16_t *blocks) {
|
|
||||||
/* function header */
|
|
||||||
blocks[0] = (2 << 6) << 8;
|
|
||||||
blocks[0] |= (rft->channel & INT8_L4); /* pipe number */
|
|
||||||
|
|
||||||
/* ODA ID */
|
|
||||||
blocks[1] = ODA_AID_RFT;
|
|
||||||
|
|
||||||
blocks[2] = (8 & INT8_L4) << 12; /* variant code */
|
|
||||||
blocks[2] |= 0 & INT16_L12;
|
|
||||||
blocks[3] = 0 & INT16_ALL;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
* RFT data group (for file data TXing)
|
|
||||||
*
|
|
||||||
* Carries:
|
|
||||||
* - Function Header
|
|
||||||
* - Pipe Number
|
|
||||||
* - Toggle Bit
|
|
||||||
* - Segment address (15 bits)
|
|
||||||
* - File data (5 bytes per group)
|
|
||||||
*/
|
|
||||||
static void get_rft_file_data_group(struct rft_t *rft, uint16_t *blocks) {
|
|
||||||
/* function header */
|
|
||||||
blocks[0] = 2 << 12;
|
|
||||||
blocks[0] |= (rft->channel & INT8_L4) << 8; /* pipe number */
|
|
||||||
|
|
||||||
/* toggle bit */
|
|
||||||
blocks[0] |= (rft->toggle & INT16_L1) << 7;
|
|
||||||
|
|
||||||
/* segment address */
|
|
||||||
blocks[0] |= ((rft->seg_addr_img & INT16_U8) >> 8) & INT16_L7;
|
|
||||||
blocks[1] = (rft->seg_addr_img & INT16_L8) << 8;
|
|
||||||
|
|
||||||
/* image data */
|
|
||||||
blocks[1] |= rft->file_data[rft->seg_addr_img * 5 + 0];
|
|
||||||
blocks[2] = rft->file_data[rft->seg_addr_img * 5 + 1] << 8;
|
|
||||||
blocks[2] |= rft->file_data[rft->seg_addr_img * 5 + 2];
|
|
||||||
blocks[3] = rft->file_data[rft->seg_addr_img * 5 + 3] << 8;
|
|
||||||
blocks[3] |= rft->file_data[rft->seg_addr_img * 5 + 4];
|
|
||||||
|
|
||||||
if (++rft->seg_addr_img > rft->num_segs) {
|
|
||||||
#ifdef RDS2_DEBUG
|
|
||||||
fprintf(stderr, "File sending complete\n");
|
|
||||||
#endif
|
|
||||||
rft->seg_addr_img = 0;
|
|
||||||
rft->toggle ^= 1;
|
|
||||||
update_rft(rft);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void get_rft_stream(uint16_t *blocks) {
|
|
||||||
static uint8_t rft_state;
|
|
||||||
|
|
||||||
switch (rft_state) {
|
|
||||||
case 0:
|
|
||||||
get_rft_var_0_data_group(&station_logo_stream, blocks);
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
get_rft_var_1_data_group(&station_logo_stream, blocks);
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
get_rft_var_0_data_group(&station_logo_stream, blocks);
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
get_rft_var_1_data_group(&station_logo_stream, blocks);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
get_rft_file_data_group(&station_logo_stream, blocks);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
rft_state++;
|
|
||||||
if (rft_state == 50) rft_state = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* RDS 2 group sequence
|
|
||||||
*/
|
|
||||||
static void get_rds2_group(uint8_t stream_num, uint16_t *blocks) {
|
|
||||||
|
|
||||||
switch (stream_num) {
|
|
||||||
case 1:
|
|
||||||
case 2:
|
|
||||||
case 3:
|
|
||||||
default:
|
|
||||||
get_rft_stream(blocks);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef RDS2_DEBUG
|
|
||||||
fprintf(stderr, "Stream %u: %04x %04x %04x %04x\n",
|
|
||||||
stream_num, blocks[0], blocks[1], blocks[2], blocks[3]);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void get_rds2_bits(uint8_t stream, uint8_t *bits) {
|
|
||||||
static uint16_t out_blocks[GROUP_LENGTH];
|
|
||||||
get_rds2_group(stream, out_blocks);
|
|
||||||
add_checkwords(out_blocks, bits, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void init_rds2_encoder(char *station_logo_path) {
|
|
||||||
/* create a new stream for the station logo */
|
|
||||||
init_rft(&station_logo_stream,
|
|
||||||
0 /* file ID */,
|
|
||||||
false /* don't use crc */,
|
|
||||||
RFT_CRC_MODE_AUTO,
|
|
||||||
station_logo_path
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void exit_rds2_encoder() {
|
|
||||||
exit_rft(&station_logo_stream);
|
|
||||||
}
|
|
||||||
71
src/rds2.h
71
src/rds2.h
@@ -1,71 +0,0 @@
|
|||||||
/*
|
|
||||||
* mpxgen - FM multiplex encoder with Stereo and RDS
|
|
||||||
* Copyright (C) 2019-2020 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 <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
typedef struct rds2_oda_t {
|
|
||||||
uint16_t aid;
|
|
||||||
uint8_t channel;
|
|
||||||
} rds2_oda_t;
|
|
||||||
|
|
||||||
#define GET_RDS2_ODA_CHANNEL(x) (x & INT8_L5)
|
|
||||||
|
|
||||||
#define MAX_FILE_NAME_LEN 64 /* does not include terminating null char */
|
|
||||||
#define MAX_IMAGE_LEN 163840
|
|
||||||
#define MAX_CRC_CHUNK_ADDR 511
|
|
||||||
|
|
||||||
/* RFT CRC mode */
|
|
||||||
enum rft_crc_mode {
|
|
||||||
RFT_CRC_MODE_ENTIRE_FILE, /* over entire file (universal) */
|
|
||||||
RFT_CRC_MODE_16_GROUPS, /* chunks of 16 groups (size <= 40,960) */
|
|
||||||
RFT_CRC_MODE_32_GROUPS, /* chunks of 32 groups (40,960 < size <= 81,920) */
|
|
||||||
RFT_CRC_MODE_64_GROUPS, /* chunks of 64 groups (size > 81,960) */
|
|
||||||
RFT_CRC_MODE_128_GROUPS, /* chunks of 128 groups (size > 81,960) */
|
|
||||||
RFT_CRC_MODE_256_GROUPS, /* chunks of 256 groups (size > 81,960) */
|
|
||||||
RFT_CRC_MODE_RFU, /* reserved */
|
|
||||||
RFT_CRC_MODE_AUTO /* automatic between 1-3 based on size */
|
|
||||||
};
|
|
||||||
|
|
||||||
/* RDS2 File Transfer */
|
|
||||||
typedef struct rft_t {
|
|
||||||
uint8_t channel;
|
|
||||||
|
|
||||||
char *file_path;
|
|
||||||
unsigned char *file_data;
|
|
||||||
size_t file_len;
|
|
||||||
size_t prev_file_len;
|
|
||||||
uint8_t file_version;
|
|
||||||
uint8_t file_id;
|
|
||||||
uint8_t variant_code;
|
|
||||||
uint8_t toggle;
|
|
||||||
|
|
||||||
uint16_t pkt_size;
|
|
||||||
uint16_t pkt_size_rem;
|
|
||||||
uint16_t seg_addr_img;
|
|
||||||
uint16_t num_segs;
|
|
||||||
|
|
||||||
/* CRC chunk map */
|
|
||||||
bool use_crc;
|
|
||||||
uint8_t crc_mode;
|
|
||||||
uint16_t crc_chunk_addr;
|
|
||||||
uint16_t num_crc_chunks;
|
|
||||||
uint8_t *crc_chunks;
|
|
||||||
uint16_t *crcs;
|
|
||||||
} rft_t;
|
|
||||||
|
|
||||||
extern void get_rds2_bits(uint8_t stream_num, uint8_t *bits);
|
|
||||||
extern void init_rds2_encoder(char *station_logo_path);
|
|
||||||
extern void exit_rds2_encoder();
|
|
||||||
@@ -1,181 +0,0 @@
|
|||||||
static const unsigned char station_logo[] = {
|
|
||||||
0xff, 0xd8, 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, 0x49, 0x46, 0x00, 0x01,
|
|
||||||
0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xff, 0xe2, 0x01, 0xd8,
|
|
||||||
0x49, 0x43, 0x43, 0x5f, 0x50, 0x52, 0x4f, 0x46, 0x49, 0x4c, 0x45, 0x00,
|
|
||||||
0x01, 0x01, 0x00, 0x00, 0x01, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x04, 0x30,
|
|
||||||
0x00, 0x00, 0x6d, 0x6e, 0x74, 0x72, 0x52, 0x47, 0x42, 0x20, 0x58, 0x59,
|
|
||||||
0x5a, 0x20, 0x07, 0xe0, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x61, 0x63, 0x73, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
|
|
||||||
0xf6, 0xd6, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xd3, 0x2d, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x09, 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00,
|
|
||||||
0x00, 0x24, 0x72, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00,
|
|
||||||
0x00, 0x14, 0x67, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x01, 0x28, 0x00, 0x00,
|
|
||||||
0x00, 0x14, 0x62, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x01, 0x3c, 0x00, 0x00,
|
|
||||||
0x00, 0x14, 0x77, 0x74, 0x70, 0x74, 0x00, 0x00, 0x01, 0x50, 0x00, 0x00,
|
|
||||||
0x00, 0x14, 0x72, 0x54, 0x52, 0x43, 0x00, 0x00, 0x01, 0x64, 0x00, 0x00,
|
|
||||||
0x00, 0x28, 0x67, 0x54, 0x52, 0x43, 0x00, 0x00, 0x01, 0x64, 0x00, 0x00,
|
|
||||||
0x00, 0x28, 0x62, 0x54, 0x52, 0x43, 0x00, 0x00, 0x01, 0x64, 0x00, 0x00,
|
|
||||||
0x00, 0x28, 0x63, 0x70, 0x72, 0x74, 0x00, 0x00, 0x01, 0x8c, 0x00, 0x00,
|
|
||||||
0x00, 0x3c, 0x6d, 0x6c, 0x75, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x65, 0x6e, 0x55, 0x53, 0x00, 0x00,
|
|
||||||
0x00, 0x08, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x73, 0x00, 0x52, 0x00, 0x47,
|
|
||||||
0x00, 0x42, 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x6f, 0xa2, 0x00, 0x00, 0x38, 0xf5, 0x00, 0x00, 0x03, 0x90, 0x58, 0x59,
|
|
||||||
0x5a, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x99, 0x00, 0x00,
|
|
||||||
0xb7, 0x85, 0x00, 0x00, 0x18, 0xda, 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x24, 0xa0, 0x00, 0x00, 0x0f, 0x84, 0x00, 0x00,
|
|
||||||
0xb6, 0xcf, 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0xf6, 0xd6, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xd3, 0x2d, 0x70, 0x61,
|
|
||||||
0x72, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02,
|
|
||||||
0x66, 0x66, 0x00, 0x00, 0xf2, 0xa7, 0x00, 0x00, 0x0d, 0x59, 0x00, 0x00,
|
|
||||||
0x13, 0xd0, 0x00, 0x00, 0x0a, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x6d, 0x6c, 0x75, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x65, 0x6e, 0x55, 0x53, 0x00, 0x00,
|
|
||||||
0x00, 0x20, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x47, 0x00, 0x6f, 0x00, 0x6f,
|
|
||||||
0x00, 0x67, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x20, 0x00, 0x49, 0x00, 0x6e,
|
|
||||||
0x00, 0x63, 0x00, 0x2e, 0x00, 0x20, 0x00, 0x32, 0x00, 0x30, 0x00, 0x31,
|
|
||||||
0x00, 0x36, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x1b, 0x12, 0x14, 0x17, 0x14,
|
|
||||||
0x11, 0x1b, 0x17, 0x16, 0x17, 0x1e, 0x1c, 0x1b, 0x20, 0x28, 0x42, 0x2b,
|
|
||||||
0x28, 0x25, 0x25, 0x28, 0x51, 0x3a, 0x3d, 0x30, 0x42, 0x60, 0x55, 0x65,
|
|
||||||
0x64, 0x5f, 0x55, 0x5d, 0x5b, 0x6a, 0x78, 0x99, 0x81, 0x6a, 0x71, 0x90,
|
|
||||||
0x73, 0x5b, 0x5d, 0x85, 0xb5, 0x86, 0x90, 0x9e, 0xa3, 0xab, 0xad, 0xab,
|
|
||||||
0x67, 0x80, 0xbc, 0xc9, 0xba, 0xa6, 0xc7, 0x99, 0xa8, 0xab, 0xa4, 0xff,
|
|
||||||
0xdb, 0x00, 0x43, 0x01, 0x1c, 0x1e, 0x1e, 0x28, 0x23, 0x28, 0x4e, 0x2b,
|
|
||||||
0x2b, 0x4e, 0xa4, 0x6e, 0x5d, 0x6e, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
|
|
||||||
0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
|
|
||||||
0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
|
|
||||||
0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
|
|
||||||
0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xff, 0xc0, 0x00, 0x11,
|
|
||||||
0x08, 0x00, 0xcd, 0x00, 0xcd, 0x03, 0x01, 0x22, 0x00, 0x02, 0x11, 0x01,
|
|
||||||
0x03, 0x11, 0x01, 0xff, 0xc4, 0x00, 0x1a, 0x00, 0x01, 0x01, 0x01, 0x01,
|
|
||||||
0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x05, 0x04, 0x06, 0x02, 0x03, 0x01, 0xff, 0xc4, 0x00, 0x3c, 0x10,
|
|
||||||
0x00, 0x01, 0x03, 0x03, 0x02, 0x02, 0x05, 0x09, 0x05, 0x08, 0x03, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x11, 0x12,
|
|
||||||
0x21, 0x06, 0x31, 0x13, 0x22, 0x41, 0x91, 0xa1, 0x14, 0x32, 0x51, 0x61,
|
|
||||||
0x71, 0x73, 0x81, 0xb1, 0xd1, 0x34, 0x36, 0x52, 0xc2, 0xe1, 0x16, 0x24,
|
|
||||||
0x33, 0x35, 0x42, 0x44, 0x82, 0xc1, 0x54, 0xb2, 0xf0, 0xff, 0xc4, 0x00,
|
|
||||||
0x14, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xc4, 0x00, 0x14, 0x11,
|
|
||||||
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xda, 0x00, 0x0c, 0x03, 0x01, 0x00,
|
|
||||||
0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00, 0xe6, 0x40, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x56,
|
|
||||||
0xc7, 0x68, 0x65, 0xd1, 0x26, 0x57, 0xcc, 0xe8, 0xfa, 0x3d, 0x3c, 0x93,
|
|
||||||
0x39, 0xce, 0x7e, 0x85, 0x2a, 0x7e, 0x14, 0x6a, 0xac, 0x8b, 0x51, 0x3b,
|
|
||||||
0x91, 0x35, 0x2a, 0x31, 0x18, 0x89, 0x9c, 0x76, 0x2a, 0xfd, 0x00, 0xe6,
|
|
||||||
0x01, 0x42, 0x9e, 0xd8, 0xda, 0xab, 0xa3, 0xa8, 0xa0, 0xaa, 0x8d, 0xcd,
|
|
||||||
0x6a, 0xff, 0x00, 0x15, 0x76, 0xd4, 0x89, 0xcf, 0x09, 0xdb, 0xdb, 0xdc,
|
|
||||||
0x5b, 0x4e, 0x13, 0xa7, 0xe8, 0xb0, 0xb5, 0x32, 0xf4, 0x9f, 0x8b, 0x09,
|
|
||||||
0x8e, 0xef, 0xd4, 0x0e, 0x50, 0x1b, 0x6e, 0xb6, 0xc9, 0x6d, 0x93, 0xa4,
|
|
||||||
0x72, 0x2a, 0x3d, 0x8e, 0x4c, 0xb1, 0xe8, 0x98, 0xcf, 0xea, 0x6d, 0xb2,
|
|
||||||
0xd8, 0x16, 0xe1, 0x0f, 0x94, 0x4d, 0x22, 0xc7, 0x12, 0xae, 0x1a, 0x8d,
|
|
||||||
0x4d, 0xdd, 0xe9, 0x5f, 0x56, 0xe0, 0x67, 0xb1, 0xdb, 0xa3, 0xb9, 0xd4,
|
|
||||||
0xbe, 0x29, 0x1e, 0xe6, 0x23, 0x59, 0xab, 0x2d, 0xf6, 0xa1, 0xf9, 0x55,
|
|
||||||
0x6f, 0x8e, 0x1b, 0xe3, 0x68, 0x1a, 0xf7, 0x2b, 0x16, 0x46, 0x33, 0x52,
|
|
||||||
0xf3, 0xeb, 0x63, 0xea, 0x74, 0x76, 0xab, 0x2a, 0xdb, 0x2e, 0x2f, 0x92,
|
|
||||||
0x39, 0x3a, 0x48, 0x5f, 0x1a, 0xa2, 0x2a, 0xf3, 0x6a, 0xe5, 0x36, 0x5f,
|
|
||||||
0x49, 0x12, 0xee, 0xc5, 0x97, 0x8a, 0x56, 0x36, 0xbd, 0x58, 0xaf, 0x92,
|
|
||||||
0x36, 0xa3, 0x9b, 0xcd, 0xb9, 0x46, 0xee, 0x80, 0x78, 0xbf, 0x5a, 0x62,
|
|
||||||
0xb5, 0xf4, 0x1d, 0x14, 0x8f, 0x7f, 0x49, 0xab, 0x3a, 0xb1, 0xb6, 0x31,
|
|
||||||
0xf5, 0x24, 0x96, 0xb8, 0x8a, 0x86, 0x5a, 0x3f, 0x27, 0xe9, 0x2b, 0x26,
|
|
||||||
0xa9, 0xd7, 0xab, 0x1d, 0x22, 0xaa, 0xe9, 0xc6, 0x39, 0x6f, 0xeb, 0x3f,
|
|
||||||
0x6d, 0x1c, 0x3d, 0x25, 0x74, 0x29, 0x51, 0x3c, 0x8b, 0x14, 0x4e, 0x5e,
|
|
||||||
0xaa, 0x22, 0x65, 0xce, 0x4f, 0x4f, 0xa8, 0x08, 0x87, 0xa8, 0xa3, 0x7c,
|
|
||||||
0xd2, 0xb2, 0x28, 0xd3, 0x53, 0xde, 0xa8, 0xd6, 0xa7, 0xa5, 0x54, 0xe9,
|
|
||||||
0x6a, 0xb8, 0x4d, 0xbd, 0x1a, 0xad, 0x25, 0x43, 0xb5, 0xa2, 0x79, 0xb2,
|
|
||||||
0x22, 0x6f, 0xf1, 0x4e, 0x44, 0xbb, 0x03, 0x7a, 0x2b, 0xf4, 0x0c, 0x95,
|
|
||||||
0x34, 0xb9, 0xae, 0x73, 0x55, 0x1d, 0xd8, 0xba, 0x57, 0x6e, 0xf0, 0x2b,
|
|
||||||
0xd3, 0x70, 0x9c, 0x29, 0x1f, 0xef, 0x35, 0x12, 0x39, 0xeb, 0xd9, 0x1e,
|
|
||||||
0x11, 0x13, 0xbd, 0x17, 0x3e, 0x04, 0xbb, 0xcd, 0x8e, 0x4b, 0x6b, 0x52,
|
|
||||||
0x68, 0xde, 0xb2, 0xc0, 0xab, 0x85, 0x5c, 0x61, 0x5a, 0xbe, 0xbf, 0xa9,
|
|
||||||
0x53, 0x8a, 0x22, 0xaf, 0x92, 0x68, 0x3c, 0x99, 0xb2, 0xbe, 0x14, 0x4c,
|
|
||||||
0xe2, 0x24, 0x55, 0xc3, 0xb3, 0xcd, 0x71, 0xf0, 0xc7, 0xc4, 0xd5, 0x77,
|
|
||||||
0x47, 0xb7, 0x86, 0x5c, 0x95, 0x19, 0x59, 0x12, 0x38, 0xd1, 0xfb, 0xe5,
|
|
||||||
0x75, 0x65, 0x3f, 0xd8, 0x11, 0x6c, 0x76, 0x58, 0x6e, 0x74, 0xf2, 0x49,
|
|
||||||
0x24, 0xaf, 0x62, 0xb5, 0xfa, 0x51, 0x1b, 0x8f, 0x41, 0x4b, 0xf6, 0x4e,
|
|
||||||
0x97, 0xfe, 0x44, 0xde, 0x07, 0xef, 0x07, 0x7d, 0x86, 0x7f, 0x7b, 0xfe,
|
|
||||||
0x90, 0xf3, 0x51, 0x64, 0xb9, 0x4b, 0x2c, 0xf2, 0xb2, 0xe0, 0xac, 0xd4,
|
|
||||||
0xf7, 0x39, 0x8c, 0xd6, 0xec, 0x22, 0x2a, 0xed, 0xbf, 0x67, 0x70, 0x10,
|
|
||||||
0xef, 0x54, 0x0c, 0xb7, 0x56, 0x24, 0x11, 0xbd, 0xcf, 0x45, 0x62, 0x3b,
|
|
||||||
0x2e, 0xe7, 0xda, 0x60, 0x3e, 0xf5, 0xac, 0xa8, 0x8a, 0xa5, 0xf1, 0xd5,
|
|
||||||
0xab, 0xd6, 0x56, 0x6c, 0xba, 0x9d, 0x93, 0xe0, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x07, 0x4f, 0xc1, 0x9e, 0x6d, 0x5f, 0xb5, 0x9f,
|
|
||||||
0x98, 0x97, 0x7a, 0xad, 0xa9, 0x75, 0xda, 0xa3, 0x13, 0x48, 0xd4, 0x63,
|
|
||||||
0x95, 0x8d, 0x46, 0xb9, 0x51, 0x11, 0x39, 0x78, 0x95, 0x38, 0x33, 0xcd,
|
|
||||||
0xab, 0xf6, 0xb3, 0xf3, 0x10, 0xee, 0xff, 0x00, 0xcd, 0x6a, 0xfd, 0xeb,
|
|
||||||
0xbe, 0x60, 0x7d, 0x2d, 0x16, 0xea, 0x8b, 0x85, 0x4e, 0x20, 0x7f, 0x47,
|
|
||||||
0xd1, 0xe1, 0xce, 0x93, 0xf0, 0xef, 0xb6, 0x3d, 0x7f, 0x42, 0xe3, 0x38,
|
|
||||||
0x6a, 0x76, 0xd4, 0xa5, 0x42, 0x5c, 0xde, 0xb3, 0x22, 0xe7, 0x5a, 0xc7,
|
|
||||||
0x95, 0xff, 0x00, 0xb1, 0x97, 0x84, 0x2a, 0x62, 0x8a, 0x6a, 0x88, 0x64,
|
|
||||||
0x7a, 0x35, 0xf2, 0xa3, 0x74, 0x65, 0x71, 0x9c, 0x67, 0x6f, 0x6e, 0xe6,
|
|
||||||
0x9a, 0xae, 0x1e, 0xa5, 0x8e, 0xa2, 0x4a, 0xba, 0x9a, 0xd7, 0x36, 0x15,
|
|
||||||
0x72, 0xbd, 0xc8, 0xa9, 0xbe, 0xfb, 0xe3, 0x3f, 0xa0, 0x0e, 0x31, 0x6a,
|
|
||||||
0x79, 0x35, 0x3b, 0xb1, 0xba, 0x3d, 0x53, 0x3f, 0x0f, 0xd0, 0xd8, 0xd7,
|
|
||||||
0x2d, 0x3f, 0x0b, 0xb5, 0xd1, 0x75, 0x1c, 0x94, 0xc8, 0xa8, 0xa9, 0xb6,
|
|
||||||
0x15, 0x53, 0x9f, 0x89, 0x97, 0x8c, 0x7e, 0xc7, 0x07, 0xbc, 0x5f, 0x91,
|
|
||||||
0xf6, 0xb0, 0xd5, 0xc1, 0x5f, 0x6a, 0x4a, 0x39, 0x5c, 0x8b, 0x23, 0x18,
|
|
||||||
0xb1, 0xbd, 0x8b, 0xb2, 0xab, 0x79, 0x22, 0xa7, 0xc3, 0x00, 0x4f, 0xe0,
|
|
||||||
0xf9, 0xa4, 0x5a, 0xa9, 0xe2, 0x57, 0xaa, 0xb1, 0x59, 0xaf, 0x0a, 0xbd,
|
|
||||||
0xb9, 0x4d, 0xfc, 0x4f, 0x8d, 0xc7, 0xef, 0x7b, 0x3d, 0xf4, 0x5f, 0x26,
|
|
||||||
0x97, 0x6d, 0x96, 0xea, 0x2b, 0x6d, 0x53, 0xe2, 0x82, 0x47, 0x3e, 0x67,
|
|
||||||
0xb3, 0x2a, 0x8f, 0x54, 0x55, 0x6b, 0x51, 0x7d, 0x49, 0xb7, 0x3f, 0x02,
|
|
||||||
0x15, 0xc7, 0xef, 0x7b, 0x3d, 0xf4, 0x5f, 0x26, 0x81, 0xab, 0x8c, 0xff,
|
|
||||||
0x00, 0xb3, 0xff, 0x00, 0x3f, 0xca, 0x6f, 0xb8, 0x48, 0xfa, 0x5e, 0x19,
|
|
||||||
0x47, 0x40, 0xf5, 0x63, 0x9b, 0x14, 0x68, 0x8e, 0x45, 0xdd, 0x39, 0x27,
|
|
||||||
0xc8, 0xc1, 0xc6, 0x7f, 0xd9, 0xff, 0x00, 0x9f, 0xe5, 0x36, 0x5a, 0xea,
|
|
||||||
0xa9, 0x2e, 0xd6, 0x96, 0xd1, 0x4c, 0xa9, 0xad, 0x18, 0x8c, 0x7b, 0x35,
|
|
||||||
0x61, 0x57, 0x1c, 0x95, 0x3b, 0x91, 0x40, 0xc5, 0xc2, 0x35, 0x53, 0x3e,
|
|
||||||
0x79, 0xe1, 0x7c, 0x8e, 0x7b, 0x34, 0x23, 0x91, 0x1c, 0xaa, 0xb8, 0x5c,
|
|
||||||
0xf6, 0x77, 0x93, 0x6f, 0xeb, 0xd0, 0xdf, 0xa7, 0x74, 0x5d, 0x47, 0x35,
|
|
||||||
0xcd, 0x72, 0x2a, 0x6d, 0x85, 0xd2, 0x8b, 0x9e, 0xf3, 0xa6, 0xa5, 0xa3,
|
|
||||||
0xa0, 0xb1, 0x43, 0x24, 0x8b, 0x2a, 0xb5, 0x1d, 0xcd, 0xf2, 0xb9, 0x32,
|
|
||||||
0xb8, 0xec, 0x43, 0x8f, 0xb8, 0xd5, 0x2d, 0x6d, 0x74, 0xd5, 0x18, 0xc2,
|
|
||||||
0x3d, 0xdb, 0x22, 0xf6, 0x27, 0x24, 0xf0, 0xc0, 0x1d, 0x1d, 0xa3, 0x88,
|
|
||||||
0x27, 0xad, 0xa8, 0x8a, 0x95, 0xd4, 0xad, 0x73, 0xd7, 0xce, 0x91, 0x1d,
|
|
||||||
0x84, 0x44, 0x4e, 0xdc, 0x63, 0xff, 0x00, 0x29, 0xe3, 0x8b, 0xeb, 0x34,
|
|
||||||
0xc5, 0x15, 0x1b, 0x57, 0x77, 0x2f, 0x48, 0xff, 0x00, 0x67, 0x24, 0xf1,
|
|
||||||
0xcf, 0x71, 0xeb, 0x84, 0xa8, 0xd2, 0x2a, 0x69, 0x2b, 0x5f, 0xb2, 0xc9,
|
|
||||||
0xd5, 0x6a, 0xaf, 0x63, 0x53, 0x9a, 0xf7, 0xfc, 0x8e, 0x7a, 0xe7, 0x56,
|
|
||||||
0xb5, 0xd5, 0xf3, 0x54, 0x7f, 0x4b, 0x9d, 0xd5, 0x4f, 0x43, 0x53, 0x64,
|
|
||||||
0xf0, 0x03, 0x6d, 0x9a, 0xf0, 0xfb, 0x6c, 0x0f, 0x8d, 0xb4, 0xab, 0x32,
|
|
||||||
0x39, 0xda, 0xb3, 0xab, 0x18, 0xdb, 0xd8, 0x6c, 0xb3, 0x5e, 0xab, 0x6a,
|
|
||||||
0xef, 0x2c, 0x8e, 0x59, 0x11, 0x62, 0x97, 0x57, 0x53, 0x09, 0x86, 0xec,
|
|
||||||
0xaa, 0x98, 0x3c, 0xf0, 0xad, 0xce, 0x1a, 0x74, 0x92, 0x92, 0x77, 0xb6,
|
|
||||||
0x34, 0x7b, 0xb5, 0xb1, 0xce, 0xd9, 0x33, 0x8d, 0xd1, 0x57, 0xe0, 0x85,
|
|
||||||
0x88, 0x6d, 0x14, 0x14, 0x55, 0x6f, 0xae, 0x6a, 0xab, 0x15, 0x32, 0xbd,
|
|
||||||
0x67, 0x22, 0x31, 0x99, 0xe6, 0xa8, 0x04, 0x4e, 0x30, 0x6e, 0x2e, 0x31,
|
|
||||||
0x3b, 0xd3, 0x12, 0x27, 0x8a, 0x90, 0x4a, 0x7c, 0x43, 0x5d, 0x1d, 0x7d,
|
|
||||||
0xc5, 0x5f, 0x0e, 0xf1, 0xc6, 0xd4, 0x62, 0x3b, 0xf1, 0x73, 0x5c, 0xf8,
|
|
||||||
0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xd9
|
|
||||||
};
|
|
||||||
static const unsigned int station_logo_len = sizeof(station_logo);
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 2.1 KiB |
Reference in New Issue
Block a user