Merge branch 'master' into master
This commit is contained in:
commit
a3e3068376
|
|
@ -1,5 +1,7 @@
|
|||
src/waveform_*.wav
|
||||
src/pydemod
|
||||
src/test
|
||||
src/pi_fm_rds
|
||||
src/*_test
|
||||
*.o
|
||||
|
||||
|
|
|
|||
37
README.md
37
README.md
|
|
@ -6,7 +6,7 @@ Pi-FM-RDS
|
|||
|
||||
This program generates an FM modulation, with RDS (Radio Data System) data generated in real time. It can include monophonic or stereophonic audio.
|
||||
|
||||
It is based on the FM transmitter created by [Oliver Mattos and Oskar Weigl](http://www.icrobotics.co.uk/wiki/index.php/Turning_the_Raspberry_Pi_Into_an_FM_Transmitter), and later adapted to using DMA by [Richard Hirst](https://github.com/richardghirst). Christophe Jacquet adapted it and added the RDS data generator and modulator. The transmitter uses the Raspberry Pi's PWM generator to produce VHF signals.
|
||||
It is based on the FM transmitter created by Oliver Mattos and Oskar Weigl, and later adapted to using DMA by [Richard Hirst](https://github.com/richardghirst). Christophe Jacquet adapted it and added the RDS data generator and modulator. The transmitter uses the Raspberry Pi's PWM generator to produce VHF signals.
|
||||
|
||||
It is compatible with both the Raspberry Pi 1 (the original one) and the Raspberry Pi 2, 3 and 4.
|
||||
|
||||
|
|
@ -16,7 +16,7 @@ PiFmRds has been developed for experimentation only. It is not a media center, i
|
|||
|
||||
## How to use it?
|
||||
|
||||
Pi-FM-RDS, depends on the `sndfile` library. To install this library on Debian-like distributions, for instance Raspbian, run `sudo apt-get install libsndfile1-dev`.
|
||||
Pi-FM-RDS, depends on the `sndfile` library. To install this library on Debian-like distributions, for instance Raspbian, run `sudo apt install libsndfile1-dev`.
|
||||
|
||||
Pi-FM-RDS also depends on the Linux `rpi-mailbox` driver, so you need a recent Linux kernel. The Raspbian releases have this starting from August 2015.
|
||||
|
||||
|
|
@ -75,7 +75,7 @@ The RDS standards states that the error for the 57 kHz subcarrier must be less t
|
|||
|
||||
In practice, I found that Pi-FM-RDS works okay even without using the `-ppm` parameter. I suppose the receivers are more tolerant than stated in the RDS spec.
|
||||
|
||||
One way to measure the ppm error is to play the `pulses.wav` file: it will play a pulse for precisely 1 second, then play a 1-second silence, and so on. Record the audio output from a radio with a good audio card. Say you sample at 44.1 kHz. Measure 10 intervals. Using [Audacity](http://audacity.sourceforge.net/) for example determine the number of samples of these 10 intervals: in the absence of clock error, it should be 441,000 samples. With my Pi, I found 441,132 samples. Therefore, my ppm error is (441132-441000)/441000 * 1e6 = 299 ppm, **assuming that my sampling device (audio card) has no clock error...**
|
||||
One way to measure the ppm error is to play the `pulses.wav` file: it will play a pulse for precisely 1 second, then play a 1-second silence, and so on. Record the audio output from a radio with a good audio card. Say you sample at 44.1 kHz. Measure 10 intervals. Using [Audacity](https://www.audacityteam.org/) for example determine the number of samples of these 10 intervals: in the absence of clock error, it should be 441,000 samples. With my Pi, I found 441,132 samples. Therefore, my ppm error is (441132-441000)/441000 * 1e6 = 299 ppm, **assuming that my sampling device (audio card) has no clock error...**
|
||||
|
||||
|
||||
### Piping audio into Pi-FM-RDS
|
||||
|
|
@ -104,7 +104,11 @@ mkfifo rds_ctl
|
|||
sudo ./pi_fm_rds -ctl rds_ctl
|
||||
```
|
||||
|
||||
Then you can send “commands” to change PS, RT and TA:
|
||||
At this point, Pi-FM-RDS waits until another program opens the named pipe in write mode
|
||||
(for example `cat >rds_ctl` in the example below) before it starts transmitting.
|
||||
|
||||
You can use the named pipe to send “commands” to change PS, RT and TA. For instance, in
|
||||
another terminal:
|
||||
|
||||
```
|
||||
cat >rds_ctl
|
||||
|
|
@ -116,9 +120,32 @@ TA OFF
|
|||
...
|
||||
```
|
||||
|
||||
> [!TIP]
|
||||
> The program that opens the named pipe in write mode can be started after Pi-FM-RDS
|
||||
> (like above) or before (in which case Pi-FM-RDS does not have to wait at startup).
|
||||
|
||||
Every line must start with either `PS`, `RT` or `TA`, followed by one space character, and the desired value. Any other line format is silently ignored. `TA ON` switches the Traffic Announcement flag to *on*, any other value switches it to *off*.
|
||||
|
||||
|
||||
### Non-ASCII characters
|
||||
|
||||
You can use the full range of characters supported by the RDS protocol. Pi-FM-RDS decodes
|
||||
the input strings based on the system's locale variables. As of early 2024, Raspberry Pi
|
||||
OS uses by default UTF-8 and the `LANG` variable is set to `en_GB.UTF-8`. With this setup,
|
||||
it should work out of the box.
|
||||
|
||||
If it does not work, look at the first message that Pi-FM-RDS prints out. It should be
|
||||
something sensible, like:
|
||||
|
||||
```
|
||||
Locale set to en_GB.UTF-8.
|
||||
```
|
||||
|
||||
If it is not consistent with your setup, or if the locale appears to be set to `(null)`,
|
||||
then your locale variables are not set correctly and Pi-FM-RDS is incapable of working
|
||||
with non-ASCII characters.
|
||||
|
||||
|
||||
## Warning and Disclaimer
|
||||
|
||||
PiFmRds is an **experimental** program, designed **only for experimentation**. It is in no way intended to become a personal *media center* or a tool to operate a *radio station*, or even broadcast sound to one's own stereo system.
|
||||
|
|
@ -197,4 +224,4 @@ The samples are played by `pi_fm_rds.c` that is adapted from Richard Hirst's [Pi
|
|||
|
||||
--------
|
||||
|
||||
© [Christophe Jacquet](http://www.jacquet80.eu/) (F8FTK), 2014-2019. Released under the GNU GPL v3.
|
||||
© [Christophe Jacquet](https://jacquet.xyz/en/) (F8FTK), 2014-2024. Released under the GNU GPL v3.
|
||||
|
|
|
|||
19
src/Makefile
19
src/Makefile
|
|
@ -30,16 +30,23 @@ CFLAGS = $(STD_CFLAGS) $(ARCH_CFLAGS) -DRASPI=$(TARGET)
|
|||
|
||||
ifneq ($(TARGET), other)
|
||||
|
||||
app: rds.o waveforms.o pi_fm_rds.o fm_mpx.o control_pipe.o mailbox.o
|
||||
$(CC) -o pi_fm_rds rds.o waveforms.o mailbox.o pi_fm_rds.o fm_mpx.o control_pipe.o -lsndfile -lm
|
||||
app: rds.o waveforms.o pi_fm_rds.o rds_strings.o fm_mpx.o control_pipe.o mailbox.o
|
||||
$(CC) -o pi_fm_rds rds.o rds_strings.o waveforms.o mailbox.o pi_fm_rds.o fm_mpx.o control_pipe.o -lsndfile -lm
|
||||
|
||||
endif
|
||||
|
||||
|
||||
rds_wav: rds.o waveforms.o rds_wav.o fm_mpx.o
|
||||
$(CC) -o rds_wav rds_wav.o rds.o waveforms.o fm_mpx.o -lsndfile -lm
|
||||
rds_wav: rds.o rds_strings.o waveforms.o rds_wav.o fm_mpx.o
|
||||
$(CC) -o rds_wav rds_wav.o rds.o rds_strings.o waveforms.o fm_mpx.o -lsndfile -lm
|
||||
|
||||
rds.o: rds.c waveforms.h
|
||||
rds_strings.o: rds_strings.c rds_strings.h
|
||||
$(CC) $(CFLAGS) rds_strings.c
|
||||
|
||||
rds_strings_test: rds_strings.o rds_strings_test.c
|
||||
$(CC) -Wall -std=gnu99 -o rds_strings_test rds_strings.o rds_strings_test.c
|
||||
./rds_strings_test
|
||||
|
||||
rds.o: rds.c waveforms.h rds_strings.o
|
||||
$(CC) $(CFLAGS) rds.c
|
||||
|
||||
control_pipe.o: control_pipe.c control_pipe.h rds.h
|
||||
|
|
@ -61,4 +68,4 @@ fm_mpx.o: fm_mpx.c fm_mpx.h
|
|||
$(CC) $(CFLAGS) fm_mpx.c
|
||||
|
||||
clean:
|
||||
rm -f *.o
|
||||
rm -f *.o *_test
|
||||
|
|
|
|||
|
|
@ -1,11 +1,8 @@
|
|||
/*
|
||||
PiFmRds - FM/RDS transmitter for the Raspberry Pi
|
||||
Copyright (C) 2014 Christophe Jacquet, F8FTK
|
||||
|
||||
|
||||
See https://github.com/ChristopheJacquet/PiFmRds
|
||||
|
||||
rds_wav.c is a test program that writes a RDS baseband signal to a WAV
|
||||
file. It requires libsndfile.
|
||||
|
||||
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
|
||||
|
|
@ -19,7 +16,7 @@
|
|||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
control_pipe.c: handles command written to a non-blocking control pipe,
|
||||
in order to change RDS PS and RT at runtime.
|
||||
*/
|
||||
|
|
@ -42,8 +39,8 @@ FILE *f_ctl;
|
|||
* Opens a file (pipe) to be used to control the RDS coder, in non-blocking mode.
|
||||
*/
|
||||
int open_control_pipe(char *filename) {
|
||||
int fd = open(filename, O_RDONLY | O_NONBLOCK);
|
||||
if(fd == -1) return -1;
|
||||
int fd = open(filename, O_RDONLY);
|
||||
if(fd < 0) return -1;
|
||||
|
||||
int flags;
|
||||
flags = fcntl(fd, F_GETFL, 0);
|
||||
|
|
@ -89,7 +86,7 @@ int poll_control_pipe() {
|
|||
return CONTROL_PIPE_TA_SET;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
*
|
||||
* See https://github.com/ChristopheJacquet/PiFmRds
|
||||
*
|
||||
* PI-FM-RDS: RaspberryPi FM transmitter, with RDS.
|
||||
* PI-FM-RDS: RaspberryPi FM transmitter, with RDS.
|
||||
*
|
||||
* This file contains the VHF FM modulator. All credit goes to the original
|
||||
* authors, Oliver Mattos and Oskar Weigl for the original idea, and to
|
||||
|
|
@ -34,7 +34,7 @@
|
|||
* http://www.icrobotics.co.uk/wiki/index.php/Turning_the_Raspberry_Pi_Into_an_FM_Transmitter
|
||||
*
|
||||
* All credit to Oliver Mattos and Oskar Weigl for creating the original code.
|
||||
*
|
||||
*
|
||||
* I have taken their idea and reworked it to use the Pi DMA engine, so
|
||||
* reducing the CPU overhead for playing a .wav file from 100% to about 1.6%.
|
||||
*
|
||||
|
|
@ -88,6 +88,7 @@
|
|||
* Richard Hirst <richardghirst@gmail.com> December 2012
|
||||
*/
|
||||
|
||||
#include <locale.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
|
@ -212,7 +213,7 @@ static struct {
|
|||
unsigned bus_addr; /* From mem_lock() */
|
||||
uint8_t *virt_addr; /* From mapmem() */
|
||||
} mbox;
|
||||
|
||||
|
||||
|
||||
|
||||
static volatile uint32_t *pwm_reg;
|
||||
|
|
@ -255,7 +256,7 @@ terminate(int num)
|
|||
dma_reg[DMA_CS] = BCM2708_DMA_RESET;
|
||||
udelay(10);
|
||||
}
|
||||
|
||||
|
||||
fm_mpx_close();
|
||||
close_control_pipe();
|
||||
|
||||
|
|
@ -266,7 +267,7 @@ terminate(int num)
|
|||
}
|
||||
|
||||
printf("Terminating: cleanly deactivated the DMA engine and killed the carrier.\n");
|
||||
|
||||
|
||||
exit(num);
|
||||
}
|
||||
|
||||
|
|
@ -327,7 +328,7 @@ int tx(uint32_t carrier_freq, char *audio_file, uint16_t pi, char *ps, char *rt,
|
|||
sa.sa_handler = terminate;
|
||||
sigaction(i, &sa, NULL);
|
||||
}
|
||||
|
||||
|
||||
dma_reg = map_peripheral(DMA_VIRT_BASE, DMA_LEN);
|
||||
pwm_reg = map_peripheral(PWM_VIRT_BASE, PWM_LEN);
|
||||
clk_reg = map_peripheral(CLK_VIRT_BASE, CLK_LEN);
|
||||
|
|
@ -351,7 +352,7 @@ int tx(uint32_t carrier_freq, char *audio_file, uint16_t pi, char *ps, char *rt,
|
|||
fatal("Could not map memory.\n");
|
||||
}
|
||||
printf("virt_addr = %p\n", mbox.virt_addr);
|
||||
|
||||
|
||||
|
||||
// GPIO4 needs to be ALT FUNC 0 to output the clock
|
||||
gpio_reg[GPFSEL0] = (gpio_reg[GPFSEL0] & ~(7 << 12)) | (4 << 12);
|
||||
|
|
@ -394,7 +395,7 @@ int tx(uint32_t carrier_freq, char *audio_file, uint16_t pi, char *ps, char *rt,
|
|||
cbp--;
|
||||
cbp->next = mem_virt_to_phys(mbox.virt_addr);
|
||||
|
||||
// Here we define the rate at which we want to update the GPCLK control
|
||||
// Here we define the rate at which we want to update the GPCLK control
|
||||
// register.
|
||||
//
|
||||
// Set the range to 2 bits. PLLD is at 500 MHz, therefore to get 228 kHz
|
||||
|
|
@ -404,7 +405,7 @@ int tx(uint32_t carrier_freq, char *audio_file, uint16_t pi, char *ps, char *rt,
|
|||
//
|
||||
// However the fractional part may have to be adjusted to take the actual
|
||||
// frequency of your Pi's oscillator into account. For example on my Pi,
|
||||
// the fractional part should be 1916 instead of 2012 to get exactly
|
||||
// the fractional part should be 1916 instead of 2012 to get exactly
|
||||
// 228 kHz. However RDS decoding is still okay even at 2012.
|
||||
//
|
||||
// So we use the 'ppm' parameter to compensate for the oscillator error
|
||||
|
|
@ -412,8 +413,8 @@ int tx(uint32_t carrier_freq, char *audio_file, uint16_t pi, char *ps, char *rt,
|
|||
float divider = (PLLFREQ/(2000*228*(1.+ppm/1.e6)));
|
||||
uint32_t idivider = (uint32_t) divider;
|
||||
uint32_t fdivider = (uint32_t) ((divider - idivider)*pow(2, 12));
|
||||
|
||||
printf("ppm corr is %.4f, divider is %.4f (%d + %d*2^-12) [nominal 1096.4912].\n",
|
||||
|
||||
printf("ppm corr is %.4f, divider is %.4f (%d + %d*2^-12) [nominal 1096.4912].\n",
|
||||
ppm, divider, idivider, fdivider);
|
||||
|
||||
pwm_reg[PWM_CTL] = 0;
|
||||
|
|
@ -433,7 +434,7 @@ int tx(uint32_t carrier_freq, char *audio_file, uint16_t pi, char *ps, char *rt,
|
|||
udelay(10);
|
||||
pwm_reg[PWM_CTL] = PWMCTL_USEF1 | PWMCTL_PWEN1;
|
||||
udelay(10);
|
||||
|
||||
|
||||
|
||||
// Initialise the DMA
|
||||
dma_reg[DMA_CS] = BCM2708_DMA_RESET;
|
||||
|
|
@ -443,7 +444,7 @@ int tx(uint32_t carrier_freq, char *audio_file, uint16_t pi, char *ps, char *rt,
|
|||
dma_reg[DMA_DEBUG] = 7; // clear debug error flags
|
||||
dma_reg[DMA_CS] = 0x10880001; // go, mid priority, wait for outstanding writes
|
||||
|
||||
|
||||
|
||||
uint32_t last_cb = (uint32_t)ctl->cb;
|
||||
|
||||
// Data structures for baseband data
|
||||
|
|
@ -453,7 +454,7 @@ int tx(uint32_t carrier_freq, char *audio_file, uint16_t pi, char *ps, char *rt,
|
|||
|
||||
// Initialize the baseband generator
|
||||
if(fm_mpx_open(audio_file, DATA_SIZE) < 0) return 1;
|
||||
|
||||
|
||||
// Initialize the RDS modulator
|
||||
char myps[9] = {0};
|
||||
set_rds_pi(pi);
|
||||
|
|
@ -461,7 +462,7 @@ int tx(uint32_t carrier_freq, char *audio_file, uint16_t pi, char *ps, char *rt,
|
|||
uint16_t count = 0;
|
||||
uint16_t count2 = 0;
|
||||
int varying_ps = 0;
|
||||
|
||||
|
||||
if(ps) {
|
||||
set_rds_ps(ps);
|
||||
printf("PI: %04X, PS: \"%s\".\n", pi, ps);
|
||||
|
|
@ -470,9 +471,11 @@ int tx(uint32_t carrier_freq, char *audio_file, uint16_t pi, char *ps, char *rt,
|
|||
varying_ps = 1;
|
||||
}
|
||||
printf("RT: \"%s\"\n", rt);
|
||||
|
||||
|
||||
// Initialize the control pipe reader
|
||||
if(control_pipe) {
|
||||
printf("Waiting for control pipe `%s` to be opened by the writer, e.g. "
|
||||
"by running `cat >%s`.\n", control_pipe, control_pipe);
|
||||
if(open_control_pipe(control_pipe) == 0) {
|
||||
printf("Reading control commands on %s.\n", control_pipe);
|
||||
} else {
|
||||
|
|
@ -480,8 +483,8 @@ int tx(uint32_t carrier_freq, char *audio_file, uint16_t pi, char *ps, char *rt,
|
|||
control_pipe = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
printf("Starting to transmit on %3.1f MHz.\n", carrier_freq/1e6);
|
||||
|
||||
for (;;) {
|
||||
|
|
@ -498,11 +501,11 @@ int tx(uint32_t carrier_freq, char *audio_file, uint16_t pi, char *ps, char *rt,
|
|||
}
|
||||
count++;
|
||||
}
|
||||
|
||||
|
||||
if(control_pipe && poll_control_pipe() == CONTROL_PIPE_PS_SET) {
|
||||
varying_ps = 0;
|
||||
}
|
||||
|
||||
|
||||
usleep(5000);
|
||||
|
||||
uint32_t cur_cb = mem_phys_to_virt(dma_reg[DMA_CONBLK_AD]);
|
||||
|
|
@ -522,7 +525,7 @@ int tx(uint32_t carrier_freq, char *audio_file, uint16_t pi, char *ps, char *rt,
|
|||
data_len = DATA_SIZE;
|
||||
data_index = 0;
|
||||
}
|
||||
|
||||
|
||||
float dval = data[data_index] * (DEVIATION / 10.);
|
||||
data_index++;
|
||||
data_len--;
|
||||
|
|
@ -552,15 +555,15 @@ int main(int argc, char **argv) {
|
|||
char *rt = "PiFmRds: live FM-RDS transmission from the RaspberryPi";
|
||||
uint16_t pi = 0x1234;
|
||||
float ppm = 0;
|
||||
|
||||
|
||||
|
||||
|
||||
// Parse command-line arguments
|
||||
for(int i=1; i<argc; i++) {
|
||||
char *arg = argv[i];
|
||||
char *param = NULL;
|
||||
|
||||
|
||||
if(arg[0] == '-' && i+1 < argc) param = argv[i+1];
|
||||
|
||||
|
||||
if((strcmp("-wav", arg)==0 || strcmp("-audio", arg)==0) && param != NULL) {
|
||||
i++;
|
||||
audio_file = param;
|
||||
|
|
@ -590,8 +593,13 @@ int main(int argc, char **argv) {
|
|||
" [-ps ps_text] [-rt rt_text] [-ctl control_pipe]\n", arg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Set locale based on the environment variables. This is necessary to decode
|
||||
// non-ASCII characters using mbtowc() in rds_strings.c.
|
||||
char* locale = setlocale(LC_ALL, "");
|
||||
printf("Locale set to %s.\n", locale);
|
||||
|
||||
int errcode = tx(carrier_freq, audio_file, pi, ps, rt, ppm, control_pipe);
|
||||
|
||||
|
||||
terminate(errcode);
|
||||
}
|
||||
|
|
|
|||
12
src/rds.c
12
src/rds.c
|
|
@ -23,6 +23,8 @@
|
|||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "rds_strings.h"
|
||||
#include "waveforms.h"
|
||||
|
||||
#define RT_LENGTH 64
|
||||
|
|
@ -237,17 +239,11 @@ void set_rds_pi(uint16_t pi_code) {
|
|||
}
|
||||
|
||||
void set_rds_rt(char *rt) {
|
||||
strncpy(rds_params.rt, rt, 64);
|
||||
for(int i=0; i<64; i++) {
|
||||
if(rds_params.rt[i] == 0) rds_params.rt[i] = 32;
|
||||
}
|
||||
fill_rds_string(rds_params.rt, rt, 64);
|
||||
}
|
||||
|
||||
void set_rds_ps(char *ps) {
|
||||
strncpy(rds_params.ps, ps, 8);
|
||||
for(int i=0; i<8; i++) {
|
||||
if(rds_params.ps[i] == 0) rds_params.ps[i] = 32;
|
||||
}
|
||||
fill_rds_string(rds_params.ps, ps, 8);
|
||||
}
|
||||
|
||||
void set_rds_ta(int ta) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,287 @@
|
|||
/*
|
||||
PiFmRds - FM/RDS transmitter for the Raspberry Pi
|
||||
Copyright (C) 2024 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <wchar.h>
|
||||
|
||||
// Convert an Unicode code point to a character encoded using the RDS character set.
|
||||
char codepoint_to_rds_char(wchar_t codepoint) {
|
||||
// The following table is sorted by ascending RDS character code.
|
||||
switch (codepoint) {
|
||||
case 0x000A: return 0x0A; // LINE FEED
|
||||
case 0x000B: return 0x0B; // END OF HEADLINE
|
||||
case 0x000D: return 0x0D; // CARRIAGE RETURN
|
||||
case 0x001F: return 0x1F; // WORD BREAK - SOFT HIPHEN
|
||||
case 0x0020: return 0x20; // SPACE
|
||||
case 0x0021: return 0x21; // EXCLAMATION MARK
|
||||
case 0x0022: return 0x22; // QUOTATION MARK
|
||||
case 0x0023: return 0x23; // NUMBER SIGN
|
||||
case 0x00A4: return 0x24; // CURRENCY SIGN
|
||||
case 0x0025: return 0x25; // PERCENT SIGN
|
||||
case 0x0026: return 0x26; // AMPERSAND
|
||||
case 0x0027: return 0x27; // APOSTOPHE
|
||||
case 0x0028: return 0x28; // LEFT PARENTHESIS
|
||||
case 0x0029: return 0x29; // RIGHT PARENTHESIS
|
||||
case 0x002A: return 0x2A; // ASTERISK
|
||||
case 0x002B: return 0x2B; // PLUS SIGN
|
||||
case 0x002C: return 0x2C; // COMMA
|
||||
case 0x002D: return 0x2D; // HYPHEN-MINUS
|
||||
case 0x002E: return 0x2E; // FULL STOP
|
||||
case 0x002F: return 0x2F; // SOLIDUS
|
||||
case 0x0030: return 0x30; // DIGIT ZERO
|
||||
case 0x0031: return 0x31; // DIGIT ONE
|
||||
case 0x0032: return 0x32; // DIGIT TWO
|
||||
case 0x0033: return 0x33; // DIGIT THREE
|
||||
case 0x0034: return 0x34; // DIGIT FOUR
|
||||
case 0x0035: return 0x35; // DIGIT FIVE
|
||||
case 0x0036: return 0x36; // DIGIT SIX
|
||||
case 0x0037: return 0x37; // DIGIT SEVEN
|
||||
case 0x0038: return 0x38; // DIGIT EIGHT
|
||||
case 0x0039: return 0x39; // DIGIT NINE
|
||||
case 0x003A: return 0x3A; // COLON
|
||||
case 0x003B: return 0x3B; // SEMICOLON
|
||||
case 0x003C: return 0x3C; // LESS-THAN SIGN
|
||||
case 0x003D: return 0x3D; // EQUALS SIGN
|
||||
case 0x003E: return 0x3E; // GREATER-THAN SIGN
|
||||
case 0x003F: return 0x3F; // QUESTION MARK
|
||||
case 0x0040: return 0x40; // COMMERCIAL AT
|
||||
case 0x0041: return 0x41; // LATIN CAPITAL LETTER A
|
||||
case 0x0042: return 0x42; // LATIN CAPITAL LETTER B
|
||||
case 0x0043: return 0x43; // LATIN CAPITAL LETTER C
|
||||
case 0x0044: return 0x44; // LATIN CAPITAL LETTER D
|
||||
case 0x0045: return 0x45; // LATIN CAPITAL LETTER E
|
||||
case 0x0046: return 0x46; // LATIN CAPITAL LETTER F
|
||||
case 0x0047: return 0x47; // LATIN CAPITAL LETTER G
|
||||
case 0x0048: return 0x48; // LATIN CAPITAL LETTER H
|
||||
case 0x0049: return 0x49; // LATIN CAPITAL LETTER I
|
||||
case 0x004A: return 0x4A; // LATIN CAPITAL LETTER J
|
||||
case 0x004B: return 0x4B; // LATIN CAPITAL LETTER K
|
||||
case 0x004C: return 0x4C; // LATIN CAPITAL LETTER L
|
||||
case 0x004D: return 0x4D; // LATIN CAPITAL LETTER M
|
||||
case 0x004E: return 0x4E; // LATIN CAPITAL LETTER N
|
||||
case 0x004F: return 0x4F; // LATIN CAPITAL LETTER O
|
||||
case 0x0050: return 0x50; // LATIN CAPITAL LETTER P
|
||||
case 0x0051: return 0x51; // LATIN CAPITAL LETTER Q
|
||||
case 0x0052: return 0x52; // LATIN CAPITAL LETTER R
|
||||
case 0x0053: return 0x53; // LATIN CAPITAL LETTER S
|
||||
case 0x0054: return 0x54; // LATIN CAPITAL LETTER T
|
||||
case 0x0055: return 0x55; // LATIN CAPITAL LETTER U
|
||||
case 0x0056: return 0x56; // LATIN CAPITAL LETTER V
|
||||
case 0x0057: return 0x57; // LATIN CAPITAL LETTER W
|
||||
case 0x0058: return 0x58; // LATIN CAPITAL LETTER X
|
||||
case 0x0059: return 0x59; // LATIN CAPITAL LETTER Y
|
||||
case 0x005A: return 0x5A; // LATIN CAPITAL LETTER Z
|
||||
case 0x005B: return 0x5B; // LEFT SQUARE BRACKET
|
||||
case 0x005C: return 0x5C; // REVERSE SOLIDUS
|
||||
case 0x005D: return 0x5D; // RIGHT SQUARE BRACKET
|
||||
case 0x2015: return 0x5E; // HORIZONTAL BAR
|
||||
case 0x005F: return 0x5F; // LOW LINE
|
||||
case 0x2551: return 0x60; // BOX DRAWINGS DOUBLE VERTICAL
|
||||
case 0x0061: return 0x61; // LATIN SMALL LETTER A
|
||||
case 0x0062: return 0x62; // LATIN SMALL LETTER B
|
||||
case 0x0063: return 0x63; // LATIN SMALL LETTER C
|
||||
case 0x0064: return 0x64; // LATIN SMALL LETTER D
|
||||
case 0x0065: return 0x65; // LATIN SMALL LETTER E
|
||||
case 0x0066: return 0x66; // LATIN SMALL LETTER F
|
||||
case 0x0067: return 0x67; // LATIN SMALL LETTER G
|
||||
case 0x0068: return 0x68; // LATIN SMALL LETTER H
|
||||
case 0x0069: return 0x69; // LATIN SMALL LETTER I
|
||||
case 0x006A: return 0x6A; // LATIN SMALL LETTER J
|
||||
case 0x006B: return 0x6B; // LATIN SMALL LETTER K
|
||||
case 0x006C: return 0x6C; // LATIN SMALL LETTER L
|
||||
case 0x006D: return 0x6D; // LATIN SMALL LETTER M
|
||||
case 0x006E: return 0x6E; // LATIN SMALL LETTER N
|
||||
case 0x006F: return 0x6F; // LATIN SMALL LETTER O
|
||||
case 0x0070: return 0x70; // LATIN SMALL LETTER P
|
||||
case 0x0071: return 0x71; // LATIN SMALL LETTER Q
|
||||
case 0x0072: return 0x72; // LATIN SMALL LETTER R
|
||||
case 0x0073: return 0x73; // LATIN SMALL LETTER S
|
||||
case 0x0074: return 0x74; // LATIN SMALL LETTER T
|
||||
case 0x0075: return 0x75; // LATIN SMALL LETTER U
|
||||
case 0x0076: return 0x76; // LATIN SMALL LETTER V
|
||||
case 0x0077: return 0x77; // LATIN SMALL LETTER W
|
||||
case 0x0078: return 0x78; // LATIN SMALL LETTER X
|
||||
case 0x0079: return 0x79; // LATIN SMALL LETTER Y
|
||||
case 0x007A: return 0x7A; // LATIN SMALL LETTER Z
|
||||
case 0x007B: return 0x7B; // LEFT CURLY BRACKET
|
||||
case 0x007C: return 0x7C; // VERTICAL LINE
|
||||
case 0x007D: return 0x7D; // RIGHT CURLY BRACKET
|
||||
case 0x00AF: return 0x7E; // MACRON
|
||||
case 0x00E1: return 0x80; // LATIN SMALL LETTER A WITH ACUTE
|
||||
case 0x00E0: return 0x81; // LATIN SMALL LETTER A WITH GRAVE
|
||||
case 0x00E9: return 0x82; // LATIN SMALL LETTER E WITH ACUTE
|
||||
case 0x00E8: return 0x83; // LATIN SMALL LETTER E WITH GRAVE
|
||||
case 0x00ED: return 0x84; // LATIN SMALL LETTER I WITH ACUTE
|
||||
case 0x00EC: return 0x85; // LATIN SMALL LETTER I WITH GRAVE
|
||||
case 0x00F3: return 0x86; // LATIN SMALL LETTER O WITH ACUTE
|
||||
case 0x00F2: return 0x87; // LATIN SMALL LETTER O WITH GRAVE
|
||||
case 0x00FA: return 0x88; // LATIN SMALL LETTER U WITH ACUTE
|
||||
case 0x00F9: return 0x89; // LATIN SMALL LETTER U WITH GRAVE
|
||||
case 0x00D1: return 0x8A; // LATIN CAPITAL LETTER N WITH TILDE
|
||||
case 0x00C7: return 0x8B; // LATIN CAPITAL LETTER C WITH CEDILLA
|
||||
case 0x015E: return 0x8C; // LATIN CAPITAL LETTER S WITH CEDILLA
|
||||
case 0x00DF: return 0x8D; // LATIN SMALL LETTER SHARP S (German)
|
||||
case 0x00A1: return 0x8E; // INVERTED EXCLAMATION MARK
|
||||
case 0x0132: return 0x8F; // LATIN CAPITAL LIGATURE IJ
|
||||
case 0x00E2: return 0x90; // LATIN SMALL LETTER A WITH CIRCUMFLEX
|
||||
case 0x00E4: return 0x91; // LATIN SMALL LETTER A WITH DIAERESIS
|
||||
case 0x00EA: return 0x92; // LATIN SMALL LETTER E WITH CIRCUMFLEX
|
||||
case 0x00EB: return 0x93; // LATIN SMALL LETTER E WITH DIAERESIS
|
||||
case 0x00EE: return 0x94; // LATIN SMALL LETTER I WITH CIRCUMFLEX
|
||||
case 0x00EF: return 0x95; // LATIN SMALL LETTER I WITH DIAERESIS
|
||||
case 0x00F4: return 0x96; // LATIN SMALL LETTER O WITH CIRCUMFLEX
|
||||
case 0x00F6: return 0x97; // LATIN SMALL LETTER O WITH DIAERESIS
|
||||
case 0x00FB: return 0x98; // LATIN SMALL LETTER U WITH CIRCUMFLEX
|
||||
case 0x00FC: return 0x99; // LATIN SMALL LETTER U WITH DIAERESIS
|
||||
case 0x00F1: return 0x9A; // LATIN SMALL LETTER N WITH TILDE
|
||||
case 0x00E7: return 0x9B; // LATIN SMALL LETTER C WITH CEDILLA
|
||||
case 0x015F: return 0x9C; // LATIN SMALL LETTER S WITH CEDILLA
|
||||
case 0x011F: return 0x9D; // LATIN SMALL LETTER G WITH BREVE
|
||||
case 0x0131: return 0x9E; // LATIN SMALL LETTER DOTLESS I
|
||||
case 0x0133: return 0x9F; // LATIN SMALL LIGATURE IJ
|
||||
case 0x00AA: return 0xA0; // FEMININE ORDINAL INDICATOR
|
||||
case 0x03B1: return 0xA1; // GREEK SMALL LETTER ALPHA
|
||||
case 0x00A9: return 0xA2; // COPYRIGHT SIGN
|
||||
case 0x2030: return 0xA3; // PER MILLE SIGN
|
||||
case 0x011E: return 0xA4; // LATIN CAPITAL LETTER G WITH BREVE
|
||||
case 0x011B: return 0xA5; // LATIN SMALL LETTER E WITH CARON
|
||||
case 0x0148: return 0xA6; // LATIN SMALL LETTER N WITH CARON
|
||||
case 0x0151: return 0xA7; // LATIN SMALL LETTER O WITH DOUBLE ACUTE
|
||||
case 0x03C0: return 0xA8; // GREEK SMALL LETTER PI
|
||||
case 0x20AC: return 0xA9; // EURO SIGN
|
||||
case 0x00A3: return 0xAA; // POUND SIGN
|
||||
case 0x0024: return 0xAB; // DOLLAR SIGN
|
||||
case 0x2190: return 0xAC; // LEFTWARDS ARROW
|
||||
case 0x2191: return 0xAD; // UPWARDS ARROW
|
||||
case 0x2192: return 0xAE; // RIGHTWARDS ARROW
|
||||
case 0x2193: return 0xAF; // DOWNWARDS ARROW
|
||||
case 0x00BA: return 0xB0; // MASCULIN ORDINAL INDICATOR
|
||||
case 0x00B9: return 0xB1; // SUPERSCRIPT ONE
|
||||
case 0x00B2: return 0xB2; // SUPERSCRIPT TWO
|
||||
case 0x00B3: return 0xB3; // SUPERSCRIPT THREE
|
||||
case 0x00B1: return 0xB4; // PLUS-MINUS SIGN
|
||||
case 0x0130: return 0xB5; // LATIN CAPITAL LETTER I WITH DOT ABOVE
|
||||
case 0x0144: return 0xB6; // LATIN SMALL LETTER N WITH ACUTE
|
||||
case 0x0171: return 0xB7; // LATIN SMALL LETTER U WITH DOUBLE ACUTE
|
||||
case 0x00B5: return 0xB8; // MIKRO SIGN
|
||||
case 0x00BF: return 0xB9; // INVERTED QUESTION MARK
|
||||
case 0x00F7: return 0xBA; // DIVISION SIGN
|
||||
case 0x00B0: return 0xBB; // DEGREE SIGN
|
||||
case 0x00BC: return 0xBC; // VULGAR FRACTION ONE QUARTER
|
||||
case 0x00BD: return 0xBD; // VULGAR FRACTION ONE HALF
|
||||
case 0x00BE: return 0xBE; // VULGAR FRACTION THREE QUARTERS
|
||||
case 0x00A7: return 0xBF; // SECTION SIGN
|
||||
case 0x00C1: return 0xC0; // LATIN CAPITAL LETTER A WITH ACUTE
|
||||
case 0x00C0: return 0xC1; // LATIN CAPITAL LETTER A WITH GRAVE
|
||||
case 0x00C9: return 0xC2; // LATIN CAPITAL LETTER E WITH ACUTE
|
||||
case 0x00C8: return 0xC3; // LATIN CAPITAL LETTER E WITH GRAVE
|
||||
case 0x00CD: return 0xC4; // LATIN CAPITAL LETTER I WITH ACUTE
|
||||
case 0x00CC: return 0xC5; // LATIN CAPITAL LETTER I WITH GRAVE
|
||||
case 0x00D3: return 0xC6; // LATIN CAPITAL LETTER O WITH ACUTE
|
||||
case 0x00D2: return 0xC7; // LATIN CAPITAL LETTER O WITH GRAVE
|
||||
case 0x00DA: return 0xC8; // LATIN CAPITAL LETTER U WITH ACUTE
|
||||
case 0x00D9: return 0xC9; // LATIN CAPITAL LETTER U WITH GRAVE
|
||||
case 0x0158: return 0xCA; // LATIN CAPITAL LETTER R WITH CARON
|
||||
case 0x010C: return 0xCB; // LATIN CAPITAL LETTER C WITH CARON
|
||||
case 0x0160: return 0xCC; // LATIN CAPITAL LETTER S WITH CARON
|
||||
case 0x017D: return 0xCD; // LATIN CAPITAL LETTER Z WITH CARON
|
||||
case 0x00D0: return 0xCE; // LATIN CAPITAL LETTER ETH
|
||||
case 0x013F: return 0xCF; // LATIN CAPITAL LETTER L WITH MIDDLE DOT
|
||||
case 0x00C2: return 0xD0; // LATIN CAPITAL LETTER A WITH CIRCUMFLEX
|
||||
case 0x00C4: return 0xD1; // LATIN CAPITAL LETTER A WITH DIAERRESIS
|
||||
case 0x00CA: return 0xD2; // LATIN CAPITAL LETTER E WITH CIRCUMFLEX
|
||||
case 0x00CB: return 0xD3; // LATIN CAPITAL LETTER A WITH DIAERESIS
|
||||
case 0x00CE: return 0xD4; // LATIN CAPITAL LETTER I WITH CIRCUMFLEX
|
||||
case 0x00CF: return 0xD5; // LATIN CAPITAL LETTER I WITH DIAERESIS
|
||||
case 0x00D4: return 0xD6; // LATIN CAPITAL LETTER O WITH CIRCUMFLEX
|
||||
case 0x00D6: return 0xD7; // LATIN CAPITAL LETTER O WITH DIAERRESIS
|
||||
case 0x00DB: return 0xD8; // LATIN CAPITAL LETTER U WITH CIRCUMFLEX
|
||||
case 0x00DC: return 0xD9; // LATIN CAPITAL LETTER U WITH DIAERESIS
|
||||
case 0x0159: return 0xDA; // LATIN SMALL LETTER R WITH CARON
|
||||
case 0x010D: return 0xDB; // LATIN SMALL LETTER C WITH CARON
|
||||
case 0x0161: return 0xDC; // LATIN SMALL LETTER S WITH CARON
|
||||
case 0x017E: return 0xDD; // LATIN SMALL LETTER Z WITH CARON
|
||||
case 0x0111: return 0xDE; // LATIN SMALL LETTER D WITH STROKE
|
||||
case 0x0140: return 0xDF; // LATIN SMALL LETTER L WITH MIDDLE DOT
|
||||
case 0x00C3: return 0xE0; // LATIN CAPITAL LETTER A WITH TILDE
|
||||
case 0x00C5: return 0xE1; // LATIN CAPITAL LETTER A WITH RING ABOVE
|
||||
case 0x00C6: return 0xE2; // LATIN CAPITAL LETTER AE
|
||||
case 0x0152: return 0xE3; // LATIN CAPITAL LIGATURE OE
|
||||
case 0x0177: return 0xE4; // LATIN SMALL LETTER Y WITH CIRCUMFLEX
|
||||
case 0x00DD: return 0xE5; // LATIN CAPITAL LETTER Y WITH ACUTE
|
||||
case 0x00D5: return 0xE6; // LATIN CAPITAL LETTER O WITH TILDE
|
||||
case 0x00D8: return 0xE7; // LATIN CAPITAL LETTER O WITH STROKE
|
||||
case 0x00DE: return 0xE8; // LATIN CAPITAL LETTER THORN
|
||||
case 0x014A: return 0xE9; // LATIN CAPITAL LETTER ENG
|
||||
case 0x0154: return 0xEA; // LATIN CAPITAL LETTER R WITH ACUTE
|
||||
case 0x0106: return 0xEB; // LATIN CAPITAL LETTER C WITH ACUTE
|
||||
case 0x015A: return 0xEC; // LATIN CAPITAL LETTER S WITH ACUTE
|
||||
case 0x0179: return 0xED; // LATIN CAPITAL LETTER Z WITH ACUTE
|
||||
case 0x0166: return 0xEE; // LATIN CAPITAL LETTER T WITH STROKE
|
||||
case 0x00F0: return 0xEF; // LATIN SMALL LETTER ETH
|
||||
case 0x00E3: return 0xF0; // LATIN SMALL LETTER A WITH TILDE
|
||||
case 0x00E5: return 0xF1; // LATIN SMALL LETTER A WITH RING
|
||||
case 0x00E6: return 0xF2; // LATIN SMALL LETTER AE
|
||||
case 0x0153: return 0xF3; // LATIN SMALL LIGATURE OE
|
||||
case 0x0175: return 0xF4; // LATIN SMALL LETTER W WITH CIRCUMFLEX
|
||||
case 0x00FD: return 0xF5; // LATIN SMALL LETTER Y WITH ACUTE
|
||||
case 0x00F5: return 0xF6; // LATIN SMALL LETTER O WITH TILDE
|
||||
case 0x00F8: return 0xF7; // LATIN SMALL LETTER O WITH STROKE
|
||||
case 0x00FE: return 0xF8; // LATIN SMALL LETTER THORN
|
||||
case 0x014B: return 0xF9; // LATIN SMALL LETTER ENG
|
||||
case 0x0155: return 0xFA; // LATIN SMALL LETTER R WITH ACUTE
|
||||
case 0x0107: return 0xFB; // LATIN SMALL LETTER C WITH ACUTE
|
||||
case 0x015B: return 0xFC; // LATIN SMALL LETTER S WITH ACUTE
|
||||
case 0x017A: return 0xFD; // LATIN SMALL LETTER Z WITH ACUTE
|
||||
case 0x0167: return 0xFE; // LATIN SMALL LETTER T WITH STROKE
|
||||
default: return 0x20; // Return a SPACE character for all other code points.
|
||||
}
|
||||
}
|
||||
|
||||
void fill_rds_string(char* rds_string, char* src_string, size_t rds_string_size) {
|
||||
mbtowc(NULL, 0, 0); // Reset decoder.
|
||||
|
||||
// First try to copy the source string.
|
||||
size_t remaining_src_size = strlen(src_string);
|
||||
size_t remaining_rds_size = rds_string_size;
|
||||
wchar_t codepoint;
|
||||
while (remaining_src_size > 0 && remaining_rds_size > 0) {
|
||||
int size = mbtowc(&codepoint, src_string, remaining_src_size);
|
||||
if (size == 0) break; // End of source string.
|
||||
if (size < 0) { // Decode error. Try to skip 1 byte and resync.
|
||||
src_string++;
|
||||
remaining_src_size--;
|
||||
continue;
|
||||
}
|
||||
*rds_string = codepoint_to_rds_char(codepoint);
|
||||
rds_string++;
|
||||
remaining_rds_size--;
|
||||
src_string += size;
|
||||
remaining_src_size -= size;
|
||||
}
|
||||
|
||||
// Pad the RDS string with SPACE characters.
|
||||
while (remaining_rds_size > 0) {
|
||||
*rds_string = 0x20;
|
||||
rds_string++;
|
||||
remaining_rds_size--;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
PiFmRds - FM/RDS transmitter for the Raspberry Pi
|
||||
Copyright (C) 2024 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef RDS_H
|
||||
#define RDS_H
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
extern void fill_rds_string(char* rds_string, char* src_string, size_t rds_string_size);
|
||||
|
||||
|
||||
#endif /* RDS_H */
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
PiFmRds - FM/RDS transmitter for the Raspberry Pi
|
||||
Copyright (C) 2024 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <locale.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "rds_strings.h"
|
||||
|
||||
void print_hex_bytes(char* s, size_t size) {
|
||||
for (int i=0; i < size; i++) {
|
||||
printf("%02X ", s[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void assert_string(char* test_name, char* actual, char* expected, size_t size) {
|
||||
bool equal = true;
|
||||
for (int i=0; i < size; i++) {
|
||||
if (actual[i] != expected[i]) {
|
||||
equal = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
printf("Test: %s -> %s\n", test_name, equal ? "PASS" : "FAIL");
|
||||
if (equal) return;
|
||||
printf("Actual: "); print_hex_bytes(actual, size);
|
||||
printf("Expected: "); print_hex_bytes(expected, size);
|
||||
}
|
||||
|
||||
void test_src_shorter() {
|
||||
const size_t dst_size = 7;
|
||||
char dst[] = {0, 1, 2, 3, 4, 5, 6, 7};
|
||||
char dst_ref[] = {'A', 'B', 'C', 'D', ' ', ' ', ' ', 7};
|
||||
fill_rds_string(dst, "ABCD", dst_size);
|
||||
assert_string("Copy shorter string", dst, dst_ref, dst_size+1);
|
||||
}
|
||||
|
||||
void test_src_longer() {
|
||||
const size_t dst_size = 7;
|
||||
char dst[] = {0, 1, 2, 3, 4, 5, 6, 7};
|
||||
char dst_ref[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 7};
|
||||
fill_rds_string(dst, "ABCDEFGHI", dst_size);
|
||||
assert_string("Copy longer string", dst, dst_ref, dst_size+1);
|
||||
}
|
||||
|
||||
void test_same_sizes() {
|
||||
const size_t dst_size = 7;
|
||||
char dst[] = {0, 1, 2, 3, 4, 5, 6, 7};
|
||||
char dst_ref[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 7};
|
||||
fill_rds_string(dst, "ABCDEFG", dst_size);
|
||||
assert_string("Copy same-size string", dst, dst_ref, dst_size+1);
|
||||
}
|
||||
|
||||
void test_non_ascii() {
|
||||
size_t dst_size = 20;
|
||||
char dst[dst_size];
|
||||
char* dst_ref = "M\x97""beltr\x91""gerf\x99\x8d""e d\x82\x9b""u";
|
||||
fill_rds_string(dst, "M\xc3\xb6""beltr\xc3\xa4""gerf\xc3\xbc\xc3\x9f""e "
|
||||
"d\xc3\xa9\xc3\xa7""u", dst_size);
|
||||
assert_string("Convert non-ASCII characters", dst, dst_ref, dst_size);
|
||||
}
|
||||
|
||||
void test_skip_invalid() {
|
||||
size_t dst_size = 6;
|
||||
char dst[dst_size];
|
||||
char dst_ref[] = {'A', 'B', 'C', ' ', ' ', ' '};
|
||||
fill_rds_string(dst, "A\xc0""B\xc1\xc2""C\xc3\xc4", dst_size);
|
||||
assert_string("Skip invalid bytes", dst, dst_ref, dst_size);
|
||||
}
|
||||
|
||||
int main() {
|
||||
const char* locale = "C.UTF-8";
|
||||
const char* locale_set = setlocale(LC_ALL, locale);
|
||||
if (locale_set == NULL || strcmp(locale, locale_set) != 0) {
|
||||
printf("Failed to set locale. Locale is '%s', while it should be '%s'. "
|
||||
"Tests will probably fail.\n", locale_set, locale);
|
||||
}
|
||||
|
||||
test_src_shorter();
|
||||
test_src_longer();
|
||||
test_same_sizes();
|
||||
test_non_ascii();
|
||||
test_skip_invalid();
|
||||
}
|
||||
Loading…
Reference in New Issue