fl2k_fm: add stereo and RDS support

Signed-off-by: Steve Markgraf <steve@steve-m.de>
This commit is contained in:
Steve Markgraf 2018-04-18 23:07:54 +02:00
parent 12ec69362e
commit 632482623d
5 changed files with 629 additions and 11 deletions

38
include/rds_mod.h Normal file
View File

@ -0,0 +1,38 @@
/*
* RDS Modulator from:
* PiFmRds - FM/RDS transmitter for the Raspberry Pi
* https://github.com/ChristopheJacquet/PiFmRds
*
* Copyright (C) 2014 by Christophe Jacquet, F8FTK
*
* adapted for use with fl2k_fm:
* Copyright (C) 2018 by Steve Markgraf <steve@steve-m.de>
*
* SPDX-License-Identifier: GPL-3.0+
*
* 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
#define RDS_MODULATOR_RATE (57000 * 4)
void get_rds_samples(double *buffer, int count);
void set_rds_pi(uint16_t pi_code);
void set_rds_rt(char *rt);
void set_rds_ps(char *ps);
void set_rds_ta(int ta);
#endif /* RDS_H */

View File

@ -76,7 +76,7 @@ endif()
add_executable(fl2k_file fl2k_file.c)
add_executable(fl2k_tcp fl2k_tcp.c)
add_executable(fl2k_test fl2k_test.c)
add_executable(fl2k_fm fl2k_fm.c)
add_executable(fl2k_fm fl2k_fm.c rds_waveforms.c rds_mod.c)
set(INSTALL_TARGETS libosmo-fl2k_shared libosmo-fl2k_static fl2k_file fl2k_tcp fl2k_test fl2k_fm)
target_link_libraries(fl2k_file libosmo-fl2k_shared

View File

@ -30,6 +30,7 @@
#ifndef _WIN32
#include <unistd.h>
#include <fcntl.h>
#include <getopt.h>
#else
#include <windows.h>
#include <io.h>
@ -41,6 +42,7 @@
#include <pthread.h>
#include "osmo-fl2k.h"
#include "rds_mod.h"
#define BUFFER_SAMPLES_SHIFT 16
#define BUFFER_SAMPLES (1 << BUFFER_SAMPLES_SHIFT)
@ -66,10 +68,15 @@ int8_t *buf2 = NULL;
uint32_t samp_rate = 100000000;
/* default signal parameters */
#define PILOT_FREQ 19000 /* In Hz */
#define STEREO_CARRIER 38000 /* In Hz */
int delta_freq = 75000;
int carrier_freq = 97000000;
int carrier_per_signal;
int input_freq = 44100;
int stereo_flag = 0;
int rds_flag = 0;
double *freqbuf;
double *slopebuf;
@ -83,7 +90,7 @@ void usage(void)
"\t[-d device index (default: 0)]\n"
"\t[-c carrier frequency (default: 9.7 MHz)]\n"
"\t[-f FM deviation (default: 75000 Hz, WBFM)]\n"
"\t[-i input audio sample rate (default: 44100 Hz)]\n"
"\t[-i input audio sample rate (default: 44100 Hz for mono FM)]\n"
"\t[-s samplerate in Hz (default: 100 MS/s)]\n"
"\tfilename (use '-' to read from stdin)\n\n"
);
@ -295,15 +302,16 @@ inline double modulate_sample(int lastwritepos, double lastfreq, double sample)
return freq;
}
void modulator(void)
void fm_modulator_mono(int use_rds)
{
unsigned int i;
size_t len;
double freq;
double lastfreq = carrier_freq;
double slope;
int16_t audio_buf[AUDIO_BUF_SIZE];
uint32_t lastwritepos = writepos;
double sample;
double rds_samples[AUDIO_BUF_SIZE];
while (!do_exit) {
len = writelen(AUDIO_BUF_SIZE);
@ -313,10 +321,88 @@ void modulator(void)
if (len == 0)
do_exit = 1;
if (use_rds)
get_rds_samples(rds_samples, len);
for (i = 0; i < len; i++) {
sample = audio_buf[i] / 32767.0;
if (use_rds) {
sample *= 4;
sample += rds_samples[i];
sample /= 5;
}
/* Modulate and buffer the sample */
lastfreq = modulate_sample(lastwritepos, lastfreq,
audio_buf[i]/32767.0);
lastfreq = modulate_sample(lastwritepos, lastfreq, sample);
lastwritepos = writepos++;
writepos %= BUFFER_SAMPLES;
}
} else {
pthread_cond_wait(&fm_cond, &fm_mutex);
}
}
}
void fm_modulator_stereo(int use_rds)
{
unsigned int i;
size_t len, sample_cnt;
double freq;
double lastfreq = carrier_freq;
int16_t audio_buf[AUDIO_BUF_SIZE];
uint32_t lastwritepos = writepos;
dds_t pilot, stereo;
double L, R, LpR, LmR, sample;
double rds_samples[AUDIO_BUF_SIZE];
/* Prepare stereo carriers */
pilot = dds_init(input_freq, PILOT_FREQ, 0);
stereo = dds_init(input_freq, STEREO_CARRIER, 0);
while (!do_exit) {
len = writelen(AUDIO_BUF_SIZE);
if (len > 1 && !(len % 2)) {
len = fread(audio_buf, 2, len, file);
if (len == 0)
do_exit = 1;
/* stereo => two audio samples per baseband sample */
sample_cnt = len/2;
if (use_rds)
get_rds_samples(rds_samples, sample_cnt);
for (i = 0; i < sample_cnt; i++) {
/* Get samples for both channels, and calculate the
* mono (L+R) and the difference signal used to recreate
* the stereo data (L-R). */
L = audio_buf[i*2] / 32767.0;
R = audio_buf[i*2+1] / 32767.0;
LpR = (L + R) / 2;
LmR = (L - R) / 2;
/* Create a composite sample consisting of the mono
* signal at baseband, a 19kHz pilot and a the difference
* signal DSB-SC modulated on a 38kHz carrier */
sample = 4.05 * LpR; /* Mono signal */
sample += 0.9 * (dds_real(&pilot)/127.0); /* Pilot */
sample += 4.05 * LmR * (dds_real(&stereo)/127.0); /* DSB-SC stereo */
if (use_rds) {
/* add RDS signal */
sample += rds_samples[i];
/* Normalize so we get the signal within [-1, 1] */
sample /= 10;
} else {
sample /= 9;
}
lastfreq = modulate_sample(lastwritepos, lastfreq, sample);
lastwritepos = writepos++;
writepos %= BUFFER_SAMPLES;
}
@ -346,13 +432,30 @@ int main(int argc, char **argv)
int dev_index = 0;
pthread_attr_t attr;
char *filename = NULL;
int option_index = 0;
int input_freq_specified = 0;
#ifndef _WIN32
struct sigaction sigact, sigign;
#endif
while ((opt = getopt(argc, argv, "d:c:f:i:s:")) != -1) {
static struct option long_options[] =
{
{"stereo", no_argument, &stereo_flag, 1},
{"rds", no_argument, &rds_flag, 1},
{0, 0, 0, 0}
};
while (1) {
opt = getopt_long(argc, argv, "d:c:f:i:s:", long_options, &option_index);
/* end of options reached */
if (opt == -1)
break;
switch (opt) {
case 0:
break;
case 'd':
dev_index = (uint32_t)atoi(optarg);
break;
@ -364,6 +467,7 @@ int main(int argc, char **argv)
break;
case 'i':
input_freq = (uint32_t)atof(optarg);
input_freq_specified = 1;
break;
case 's':
samp_rate = (uint32_t)atof(optarg);
@ -384,7 +488,24 @@ int main(int argc, char **argv)
exit(1);
}
if(strcmp(filename, "-") == 0) { /* Read samples from stdin */
if (rds_flag && input_freq_specified) {
if (input_freq != RDS_MODULATOR_RATE) {
fprintf(stderr, "RDS modulator only works with "
"228 kHz audio sample rate!\n");
exit(1);
}
} else if (rds_flag && !input_freq_specified) {
input_freq = RDS_MODULATOR_RATE;
}
if (stereo_flag && input_freq < (RDS_MODULATOR_RATE/2)) {
fprintf(stderr, "Audio sample rate needs to be at least "
"114 kHz for stereo FM!\n");
exit(1);
}
if (strcmp(filename, "-") == 0) { /* Read samples from stdin */
file = stdin;
#ifdef _WIN32
_setmode(_fileno(stdin), _O_BINARY);
@ -452,8 +573,10 @@ int main(int argc, char **argv)
/* Calculate needed constants */
carrier_per_signal = samp_rate / input_freq;
carrier_freq = samp_rate - carrier_freq;
/* Set RDS parameters */
set_rds_pi(0x0dac);
set_rds_ps("fl2k_fm");
set_rds_rt("VGA FM transmitter");
#ifndef _WIN32
sigact.sa_handler = sighandler;
@ -468,7 +591,15 @@ int main(int argc, char **argv)
SetConsoleCtrlHandler( (PHANDLER_ROUTINE) sighandler, TRUE );
#endif
modulator();
if (stereo_flag) {
fm_modulator_stereo(rds_flag);
} else {
if (rds_flag)
fprintf(stderr, "Warning: RDS with mono (without 19 kHz pilot"
" tone) doesn't work with all receivers!\n");
fm_modulator_mono(rds_flag);
}
out:
fl2k_close(dev);

296
src/rds_mod.c Normal file
View File

@ -0,0 +1,296 @@
/*
* RDS Modulator from:
* PiFmRds - FM/RDS transmitter for the Raspberry Pi
* https://github.com/ChristopheJacquet/PiFmRds
*
* Copyright (C) 2014 by Christophe Jacquet, F8FTK
*
* adapted for use with fl2k_fm:
* Copyright (C) 2018 by Steve Markgraf <steve@steve-m.de>
*
* SPDX-License-Identifier: GPL-3.0+
*
* 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 <stdint.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
#define RT_LENGTH 64
#define PS_LENGTH 8
#define GROUP_LENGTH 4
extern double waveform_biphase[576];
struct {
uint16_t pi;
int ta;
char ps[PS_LENGTH];
char rt[RT_LENGTH];
} rds_params = { 0 };
/* The RDS error-detection code generator polynomial is
x^10 + x^8 + x^7 + x^5 + x^4 + x^3 + x^0
*/
#define POLY 0x1B9
#define POLY_DEG 10
#define MSB_BIT (1 << 15)
#define BLOCK_SIZE 16
#define BITS_PER_GROUP (GROUP_LENGTH * (BLOCK_SIZE+POLY_DEG))
#define SAMPLES_PER_BIT 192
#define FILTER_SIZE (sizeof(waveform_biphase)/sizeof(double))
#define SAMPLE_BUFFER_SIZE (SAMPLES_PER_BIT + FILTER_SIZE)
uint16_t offset_words[] = { 0x0FC, 0x198, 0x168, 0x1B4 };
// We don't handle offset word C' here for the sake of simplicity
/* Classical CRC computation */
uint16_t crc(uint16_t block)
{
uint16_t crc = 0;
int i, bit, msb;
for (i = 0; i < BLOCK_SIZE; i++) {
bit = (block & MSB_BIT) != 0;
block <<= 1;
msb = (crc >> (POLY_DEG-1)) & 1;
crc <<= 1;
if ((msb ^ bit) != 0)
crc = crc ^ POLY;
}
return crc;
}
/* Possibly generates a CT (clock time) group if the minute has just changed
Returns 1 if the CT group was generated, 0 otherwise
*/
int get_rds_ct_group(uint16_t *blocks)
{
static int latest_minutes = -1;
int l, mjd, offset;
// Check time
time_t now;
struct tm *utc;
now = time(NULL);
utc = gmtime(&now);
if(utc->tm_min != latest_minutes) {
// Generate CT group
latest_minutes = utc->tm_min;
l = utc->tm_mon <= 1 ? 1 : 0;
mjd = 14956 + utc->tm_mday +
(int)((utc->tm_year - l) * 365.25) +
(int)((utc->tm_mon + 2 + l*12) * 30.6001);
blocks[1] = 0x4400 | (mjd>>15);
blocks[2] = (mjd<<1) | (utc->tm_hour>>4);
blocks[3] = (utc->tm_hour & 0xF)<<12 | utc->tm_min<<6;
utc = localtime(&now);
offset = utc->tm_gmtoff / (30 * 60);
blocks[3] |= abs(offset);
if (offset < 0)
blocks[3] |= 0x20;
return 1;
} else {
return 0;
}
}
/* Creates an RDS group. This generates sequences of the form 0A, 0A, 0A, 0A, 2A, etc.
The pattern is of length 5, the variable 'state' keeps track of where we are in the
pattern. 'ps_state' and 'rt_state' keep track of where we are in the PS (0A) sequence
or RT (2A) sequence, respectively.
*/
void get_rds_group(int *buffer)
{
static int state = 0;
static int ps_state = 0;
static int rt_state = 0;
uint16_t blocks[GROUP_LENGTH] = { rds_params.pi, 0, 0, 0 };
uint16_t block, check;
int i, j;
// Generate block content
if (!get_rds_ct_group(blocks)) { // CT (clock time) has priority on other group types
if (state < 4) {
blocks[1] = 0x0400 | ps_state;
if (rds_params.ta)
blocks[1] |= 0x0010;
blocks[2] = 0xCDCD; // no AF
blocks[3] = rds_params.ps[ps_state*2] << 8 | rds_params.ps[ps_state*2+1];
ps_state++;
if (ps_state >= 4)
ps_state = 0;
} else { // state == 5
blocks[1] = 0x2400 | rt_state;
blocks[2] = rds_params.rt[rt_state*4+0] << 8 | rds_params.rt[rt_state*4+1];
blocks[3] = rds_params.rt[rt_state*4+2] << 8 | rds_params.rt[rt_state*4+3];
rt_state++;
if (rt_state >= 16)
rt_state = 0;
}
state++;
if (state >= 5)
state = 0;
}
// Calculate the checkword for each block and emit the bits
for (i = 0; i < GROUP_LENGTH; i++) {
block = blocks[i];
check = crc(block) ^ offset_words[i];
for (j = 0; j < BLOCK_SIZE; j++) {
*buffer++ = ((block & (1 << (BLOCK_SIZE-1))) != 0);
block <<= 1;
}
for (j = 0; j < POLY_DEG; j++) {
*buffer++ = ((check & (1 << (POLY_DEG-1))) != 0);
check <<= 1;
}
}
}
/* Get a number of RDS samples. This generates the envelope of the waveform using
pre-generated elementary waveform samples, and then it amplitude-modulates the
envelope with a 57 kHz carrier, which is very efficient as 57 kHz is 4 times the
sample frequency we are working at (228 kHz).
*/
void get_rds_samples(double *buffer, uint32_t count)
{
static int bit_buffer[BITS_PER_GROUP];
static int bit_pos = BITS_PER_GROUP;
static double sample_buffer[SAMPLE_BUFFER_SIZE] = {0};
static int prev_output = 0;
static int cur_output = 0;
static int cur_bit = 0;
static int sample_count = SAMPLES_PER_BIT;
static int inverting = 0;
static int phase = 0;
static unsigned int in_sample_index = 0;
static unsigned int out_sample_index = SAMPLE_BUFFER_SIZE-1;
unsigned int i, j, idx;
double val, sample;
double *src;
for (i = 0; i < count; i++) {
if (sample_count >= SAMPLES_PER_BIT) {
if (bit_pos >= BITS_PER_GROUP) {
get_rds_group(bit_buffer);
bit_pos = 0;
}
// do differential encoding
cur_bit = bit_buffer[bit_pos];
prev_output = cur_output;
cur_output = prev_output ^ cur_bit;
inverting = (cur_output == 1);
src = waveform_biphase;
idx = in_sample_index;
for (j = 0; j < FILTER_SIZE; j++) {
val = *src++;
if (inverting)
val = -val;
sample_buffer[idx++] += val;
if (idx >= SAMPLE_BUFFER_SIZE)
idx = 0;
}
in_sample_index += SAMPLES_PER_BIT;
if (in_sample_index >= SAMPLE_BUFFER_SIZE)
in_sample_index -= SAMPLE_BUFFER_SIZE;
bit_pos++;
sample_count = 0;
}
sample = sample_buffer[out_sample_index];
sample_buffer[out_sample_index] = 0;
out_sample_index++;
if (out_sample_index >= SAMPLE_BUFFER_SIZE)
out_sample_index = 0;
// modulate at 57 kHz
// use phase for this
switch (phase) {
case 0:
case 2: sample = 0; break;
case 1: break;
case 3: sample = -sample; break;
}
phase++;
if (phase >= 4)
phase = 0;
*buffer++ = sample;
sample_count++;
}
}
void set_rds_pi(uint16_t pi_code)
{
rds_params.pi = pi_code;
}
void set_rds_rt(char *rt)
{
int i;
strncpy(rds_params.rt, rt, 64);
for (i = 0; i < 64; i++) {
if (rds_params.rt[i] == 0)
rds_params.rt[i] = 32;
}
}
void set_rds_ps(char *ps)
{
int i;
strncpy(rds_params.ps, ps, 8);
for (i = 0; i < 8; i++) {
if (rds_params.ps[i] == 0)
rds_params.ps[i] = 32;
}
}
void set_rds_ta(int ta)
{
rds_params.ta = ta;
}

153
src/rds_waveforms.c Normal file
View File

@ -0,0 +1,153 @@
/* This file was automatically generated by "generate_waveforms.py" from
* https://github.com/ChristopheJacquet/PiFmRds/
*
* (C) 2014 Christophe Jacquet.
* Released under the GNU GPL v3 license.
*/
double waveform_biphase[] = {
0.00253265133022, 0.00255504491037, 0.00256667102126, 0.00256723854970,
0.00255649674667, 0.00253423716573, 0.00250029547253, 0.00245455311551,
0.00239693884806, 0.00232743009314, 0.00224605414143, 0.00215288917468,
0.00204806510656, 0.00193176423352, 0.00180422168917, 0.00166572569587,
0.00151661760823, 0.00135729174364, 0.00118819499588, 0.00100982622839,
0.00082273544470, 0.00062752273428, 0.00042483699288, 0.00021537441720,
-0.00000012322530, -0.00022087054977, -0.00044604072817, -0.00067476788077,
-0.00090614968071, -0.00113925016637, -0.00137310275567, -0.00160671345499,
-0.00183906425517, -0.00206911670572, -0.00229581565752, -0.00251809316382,
-0.00273487252813, -0.00294507248686, -0.00314761151410, -0.00334141223473,
-0.00352540593170, -0.00369853713255, -0.00385976825946, -0.00400808432674,
-0.00414249766903, -0.00426205268297, -0.00436583056466, -0.00445295402495,
-0.00452259196407, -0.00457396408696, -0.00460634544047, -0.00461907085337,
-0.00461153926002, -0.00458321788861, -0.00453364629481, -0.00446244022186,
-0.00436929526830, -0.00425399034471, -0.00411639090130, -0.00395645190841,
-0.00377422057251, -0.00356983877090, -0.00334354518872, -0.00309567714275,
-0.00282667207705, -0.00253706871632, -0.00222750786389, -0.00189873283191,
-0.00155158949247, -0.00118702593940, -0.00080609175162, -0.00040993684994,
0.00000019005938, 0.00042294345989, 0.00085688342327, 0.00130047843362,
0.00175210863919, 0.00221006953169, 0.00267257605183, 0.00313776711824,
0.00360371057560, 0.00406840855594, 0.00452980324611, 0.00498578305229,
0.00543418915128, 0.00587282241677, 0.00629945070701, 0.00671181649912,
0.00710764485348, 0.00748465169059, 0.00784055236082, 0.00817307048659,
0.00847994705483, 0.00875894973632, 0.00900788240743, 0.00922459484812,
0.00940699258958, 0.00955304688301, 0.00966080476069, 0.00972839915915,
0.00975405907344, 0.00973611971083, 0.00967303261139, 0.00956337570235,
0.00940586325271, 0.00919935569384, 0.00894286927184, 0.00863558549687,
0.00827686035476, 0.00786623324593, 0.00740343561706, 0.00688839925073,
0.00632126417893, 0.00570238618641, 0.00503234387065, 0.00431194522560,
0.00354223371723, 0.00272449381977, 0.00186025598240, 0.00095130099727,
-0.00000033625901, -0.00099236373728, -0.00202222980620, -0.00308712154498,
-0.00418396376074, -0.00530941875093, -0.00645988682960, -0.00763150763453,
-0.00882016223000, -0.01002147601830, -0.01123082247100, -0.01244332768770,
-0.01365387579080, -0.01485711515770, -0.01604746549480, -0.01721912575140,
-0.01836608287050, -0.01948212137170, -0.02056083375870, -0.02159563173920,
-0.02257975824610, -0.02350630024450, -0.02436820230520, -0.02515828092590,
-0.02586923957590, -0.02649368443980, -0.02702414083260, -0.02745307025480,
-0.02777288805630, -0.02797598167370, -0.02805472940410, -0.02800151967640,
-0.02780877077830, -0.02746895099680, -0.02697459912600, -0.02631834529620,
-0.02549293207490, -0.02449123579000, -0.02330628802350, -0.02193129722250,
-0.02035967037330, -0.01858503468320, -0.01660125921430, -0.01440247641080,
-0.01198310346410, -0.00933786345535, -0.00646180621834, -0.00335032886310,
0.00000080409844, 0.00359544108347, 0.00743702428450, 0.01152857108890,
0.01587265612930, 0.02047139402060, 0.02532642284170, 0.03043888841330,
0.03580942942790, 0.04143816348300, 0.04732467406730, 0.05346799854990,
0.05986661721770, 0.06651844340760, 0.07342081477420, 0.08057048573450,
0.08796362112670, 0.09559579111960, 0.10346196740200, 0.11155652068800,
0.11987321955300, 0.12840523064300, 0.13714512025400, 0.14608485732300,
0.15521581782400, 0.16452879059300, 0.17401398458500, 0.18366103756000,
0.19345902621300, 0.20339647772800, 0.21346138276100, 0.22364120983400,
0.23392292112600, 0.24429298964900, 0.25473741777900, 0.26524175712200,
0.27579112968500, 0.28637025030900, 0.29696345035600, 0.30755470256700,
0.31812764709700, 0.32866561863200, 0.33915167458400, 0.34956862427200,
0.35989905906300, 0.37012538339000, 0.38022984661400, 0.39019457563000,
0.40000160819100, 0.40963292682000, 0.41907049336100, 0.42829628390300,
0.43729232419900, 0.44604072538100, 0.45452371993000, 0.46272369782900,
0.47062324279700, 0.47820516854900, 0.48545255496600, 0.49234878413200,
0.49887757611800, 0.50502302444800, 0.51076963117100, 0.51610234143400,
0.52100657748800, 0.52546827205100, 0.52947390092900, 0.53301051483100,
0.53606577028900, 0.53862795962200, 0.54068603984100, 0.54222966045900,
0.54324919009800, 0.54373574185300, 0.54368119733200, 0.54307822931100,
0.54192032294800, 0.54020179549500, 0.53791781445300, 0.53506441412200,
0.53163851050000, 0.52763791447900, 0.52306134330900, 0.51790843029000,
0.51217973265200, 0.50587673761100, 0.49900186656100, 0.49155847739800,
0.48355086494200, 0.47498425946700, 0.46586482332000, 0.45619964562500,
0.44599673508500, 0.43526501088000, 0.42401429168100, 0.41225528278300,
0.39999956139700, 0.38725956008100, 0.37404854845100, 0.36038061302100,
0.34627063539800, 0.33173426875400, 0.31678791267100, 0.30144868639300,
0.28573440054000, 0.26966352734900, 0.25325516949400, 0.23652902755300,
0.21950536619500, 0.20220497914700, 0.18464915302200, 0.16685963008900,
0.14885857004900, 0.13066851092000, 0.11231232909100, 0.09381319865270,
0.07519455007850, 0.05648002834940, 0.03769345061440, 0.01885876347700,
0.00000000000000, -0.01885876347700, -0.03769345061440, -0.05648002834940,
-0.07519455007850, -0.09381319865270, -0.11231232909100, -0.13066851092000,
-0.14885857004900, -0.16685963008900, -0.18464915302200, -0.20220497914700,
-0.21950536619500, -0.23652902755300, -0.25325516949400, -0.26966352734900,
-0.28573440054000, -0.30144868639300, -0.31678791267100, -0.33173426875400,
-0.34627063539800, -0.36038061302100, -0.37404854845100, -0.38725956008100,
-0.39999956139700, -0.41225528278300, -0.42401429168100, -0.43526501088000,
-0.44599673508500, -0.45619964562500, -0.46586482332000, -0.47498425946700,
-0.48355086494200, -0.49155847739800, -0.49900186656100, -0.50587673761100,
-0.51217973265200, -0.51790843029000, -0.52306134330900, -0.52763791447900,
-0.53163851050000, -0.53506441412200, -0.53791781445300, -0.54020179549500,
-0.54192032294800, -0.54307822931100, -0.54368119733200, -0.54373574185300,
-0.54324919009800, -0.54222966045900, -0.54068603984100, -0.53862795962200,
-0.53606577028900, -0.53301051483100, -0.52947390092900, -0.52546827205100,
-0.52100657748800, -0.51610234143400, -0.51076963117100, -0.50502302444800,
-0.49887757611800, -0.49234878413200, -0.48545255496600, -0.47820516854900,
-0.47062324279700, -0.46272369782900, -0.45452371993000, -0.44604072538100,
-0.43729232419900, -0.42829628390300, -0.41907049336100, -0.40963292682000,
-0.40000160819100, -0.39019457563000, -0.38022984661400, -0.37012538339000,
-0.35989905906300, -0.34956862427200, -0.33915167458400, -0.32866561863200,
-0.31812764709700, -0.30755470256700, -0.29696345035600, -0.28637025030900,
-0.27579112968500, -0.26524175712200, -0.25473741777900, -0.24429298964900,
-0.23392292112600, -0.22364120983400, -0.21346138276100, -0.20339647772800,
-0.19345902621300, -0.18366103756000, -0.17401398458500, -0.16452879059300,
-0.15521581782400, -0.14608485732300, -0.13714512025400, -0.12840523064300,
-0.11987321955300, -0.11155652068800, -0.10346196740200, -0.09559579111960,
-0.08796362112670, -0.08057048573450, -0.07342081477420, -0.06651844340760,
-0.05986661721770, -0.05346799854990, -0.04732467406730, -0.04143816348300,
-0.03580942942790, -0.03043888841330, -0.02532642284170, -0.02047139402060,
-0.01587265612930, -0.01152857108890, -0.00743702428450, -0.00359544108347,
-0.00000080409844, 0.00335032886310, 0.00646180621834, 0.00933786345535,
0.01198310346410, 0.01440247641080, 0.01660125921430, 0.01858503468320,
0.02035967037330, 0.02193129722250, 0.02330628802350, 0.02449123579000,
0.02549293207490, 0.02631834529620, 0.02697459912600, 0.02746895099680,
0.02780877077830, 0.02800151967640, 0.02805472940410, 0.02797598167370,
0.02777288805630, 0.02745307025480, 0.02702414083260, 0.02649368443980,
0.02586923957590, 0.02515828092590, 0.02436820230520, 0.02350630024450,
0.02257975824610, 0.02159563173920, 0.02056083375870, 0.01948212137170,
0.01836608287050, 0.01721912575140, 0.01604746549480, 0.01485711515770,
0.01365387579080, 0.01244332768770, 0.01123082247100, 0.01002147601830,
0.00882016223000, 0.00763150763453, 0.00645988682960, 0.00530941875093,
0.00418396376074, 0.00308712154498, 0.00202222980620, 0.00099236373728,
0.00000033625901, -0.00095130099727, -0.00186025598240, -0.00272449381977,
-0.00354223371723, -0.00431194522560, -0.00503234387065, -0.00570238618641,
-0.00632126417893, -0.00688839925073, -0.00740343561706, -0.00786623324593,
-0.00827686035476, -0.00863558549687, -0.00894286927184, -0.00919935569384,
-0.00940586325271, -0.00956337570235, -0.00967303261139, -0.00973611971083,
-0.00975405907344, -0.00972839915915, -0.00966080476069, -0.00955304688301,
-0.00940699258958, -0.00922459484812, -0.00900788240743, -0.00875894973632,
-0.00847994705483, -0.00817307048659, -0.00784055236082, -0.00748465169059,
-0.00710764485348, -0.00671181649912, -0.00629945070701, -0.00587282241677,
-0.00543418915128, -0.00498578305229, -0.00452980324611, -0.00406840855594,
-0.00360371057560, -0.00313776711824, -0.00267257605183, -0.00221006953169,
-0.00175210863919, -0.00130047843362, -0.00085688342327, -0.00042294345989,
-0.00000019005938, 0.00040993684994, 0.00080609175162, 0.00118702593940,
0.00155158949247, 0.00189873283191, 0.00222750786389, 0.00253706871632,
0.00282667207705, 0.00309567714275, 0.00334354518872, 0.00356983877090,
0.00377422057251, 0.00395645190841, 0.00411639090130, 0.00425399034471,
0.00436929526830, 0.00446244022186, 0.00453364629481, 0.00458321788861,
0.00461153926002, 0.00461907085337, 0.00460634544047, 0.00457396408696,
0.00452259196407, 0.00445295402495, 0.00436583056466, 0.00426205268297,
0.00414249766903, 0.00400808432674, 0.00385976825946, 0.00369853713255,
0.00352540593170, 0.00334141223473, 0.00314761151410, 0.00294507248686,
0.00273487252813, 0.00251809316382, 0.00229581565752, 0.00206911670572,
0.00183906425517, 0.00160671345499, 0.00137310275567, 0.00113925016637,
0.00090614968071, 0.00067476788077, 0.00044604072817, 0.00022087054977,
0.00000012322530, -0.00021537441720, -0.00042483699288, -0.00062752273428,
-0.00082273544470, -0.00100982622839, -0.00118819499588, -0.00135729174364,
-0.00151661760823, -0.00166572569587, -0.00180422168917, -0.00193176423352,
-0.00204806510656, -0.00215288917468, -0.00224605414143, -0.00232743009314,
-0.00239693884806, -0.00245455311551, -0.00250029547253, -0.00253423716573,
-0.00255649674667, -0.00256723854970, -0.00256667102126, -0.00255504491037,
};