#include "freedv_api_internal.h"
#include "comp_prim.h"
-#define VERSION 10 /* The API version number. The first version
+#define VERSION 11 /* The API version number. The first version
is 10. Increment if the API changes in a
way that would require changes by the API
user. */
/*
* Version 10 Initial version August 2, 2015.
- * Version 11 September
- * Changes go here.
- *
+ * Version 11 September 2015
+ * Added: freedv_zero_total_bit_errors(), freedv_get_sync()
+ * Changed all input and output sample rates to 8000 sps. Rates for FREEDV_MODE_700 and 700B were 7500.
*/
#define NORM_PWR 1.74 /* experimentally derived fudge factor so 1600 and
golay23_init();
f->nin = FDMDV_NOM_SAMPLES_PER_FRAME;
f->n_nom_modem_samples = 2*FDMDV_NOM_SAMPLES_PER_FRAME;
+ f->n_nat_modem_samples = f->n_nom_modem_samples;
f->n_max_modem_samples = FDMDV_NOM_SAMPLES_PER_FRAME+FDMDV_MAX_SAMPLES_PER_FRAME;
f->modem_sample_rate = FS;
nbit = fdmdv_bits_per_frame(f->fdmdv);
codec2_mode = CODEC2_MODE_700B;
f->cohpsk = cohpsk_create();
f->nin = COHPSK_NOM_SAMPLES_PER_FRAME;
- f->n_nom_modem_samples = COHPSK_NOM_SAMPLES_PER_FRAME;
- f->n_max_modem_samples = COHPSK_MAX_SAMPLES_PER_FRAME;
+ f->n_nat_modem_samples = COHPSK_NOM_SAMPLES_PER_FRAME; // native modem samples as used by the modem
+ f->n_nom_modem_samples = f->n_nat_modem_samples * 8000 / 7500; // number of samples after native samples are interpolated to 8000 sps
+ f->n_max_modem_samples = COHPSK_MAX_SAMPLES_PER_FRAME * 8000 / 7500 + 1;
f->modem_sample_rate = COHPSK_FS; /* note wierd sample rate */
f->clip = 1;
nbit = COHPSK_BITS_PER_FRAME;
if ((f->packed_codec_bits == NULL) || (f->codec_bits == NULL))
return NULL;
+ if ((mode == FREEDV_MODE_700) || (mode == FREEDV_MODE_700B)) { // change modem rates to 8000 sps
+ f->ptFilter7500to8000 = (struct quisk_cfFilter *)malloc(sizeof(struct quisk_cfFilter));
+ f->ptFilter8000to7500 = (struct quisk_cfFilter *)malloc(sizeof(struct quisk_cfFilter));
+ quisk_filt_cfInit(f->ptFilter8000to7500, quiskFilt120t480, sizeof(quiskFilt120t480)/sizeof(float));
+ quisk_filt_cfInit(f->ptFilter7500to8000, quiskFilt120t480, sizeof(quiskFilt120t480)/sizeof(float));
+ }
+ else {
+ f->ptFilter7500to8000 = NULL;
+ f->ptFilter8000to7500 = NULL;
+ }
+
varicode_decode_init(&f->varicode_dec_states, 1);
f->nvaricode_bits = 0;
f->varicode_bit_index = 0;
cohpsk_destroy(freedv->cohpsk);
#endif
codec2_destroy(freedv->codec2);
+ if (freedv->ptFilter8000to7500) {
+ quisk_filt_destroy(freedv->ptFilter8000to7500);
+ free(freedv->ptFilter8000to7500);
+ freedv->ptFilter8000to7500 = NULL;
+ }
+ if (freedv->ptFilter7500to8000) {
+ quisk_filt_destroy(freedv->ptFilter7500to8000);
+ free(freedv->ptFilter7500to8000);
+ freedv->ptFilter7500to8000 = NULL;
+ }
free(freedv);
}
should be such that the peak speech level is between +/- 16384 and
+/- 32767.
- The FDM modem signal mod_out[] is sampled at
- freedv_get_modem_sample_rate() Hz and is
+ The FDM modem signal mod_out[] is sampled at 8000 Hz and is
freedv_get_n_nom_modem_samples() long. mod_out[] will be scaled
such that the peak level is just less than +/-32767.
- Note that freedv_get_modem_sample_rate() is 8000 Hz for 1600 mode
- but 7500 Hz for 700 and 700B mode. You must convert sample rates as
- required.
-
The complex-valued output can directly drive an I/Q modulator to
produce a single sideband signal. To generate the other sideband,
take the complex conjugate of mod_out[].
int bit, byte, i, j, k;
int bits_per_codec_frame, bits_per_modem_frame;
int data, codeword1, data_flag_index, nspare;
- COMP tx_fdm[f->n_nom_modem_samples];
+ COMP tx_fdm[f->n_nat_modem_samples];
assert((f->mode == FREEDV_MODE_1600) || (f->mode == FREEDV_MODE_700) || (f->mode == FREEDV_MODE_700B));
cohpsk_mod(f->cohpsk, tx_fdm, f->codec_bits);
if (f->clip)
cohpsk_clip(tx_fdm);
- for(i=0; i<f->n_nom_modem_samples; i++)
+ for(i=0; i<f->n_nat_modem_samples; i++)
mod_out[i] = fcmult(FDMDV_SCALE*NORM_PWR, tx_fdm[i]);
+ i = quisk_cfInterpDecim(mod_out, f->n_nat_modem_samples, f->ptFilter7500to8000, 16, 15);
+ //assert(i == f->n_nom_modem_samples);
+ // Caution: assert fails if f->n_nat_modem_samples * 16.0 / 15.0 is not an integer
}
#endif
}
int freedv_nin(struct freedv *f) {
- return f->nin;
+ if ((f->mode == FREEDV_MODE_700) || (f->mode == FREEDV_MODE_700B))
+ // For mode 700, the input rate is 8000 sps, but the modem rate is 7500 sps
+ // For mode 700, we request a larger number of Rx samples that will be decimated to f->nin samples
+ return (16 * f->nin + f->ptFilter8000to7500->decim_index) / 15;
+ else
+ return f->nin;
}
/*---------------------------------------------------------------------------*\
decodes them, producing a frame of decoded speech samples. See
freedv_rx.c for an example.
- demod_in[] is a variable length block of received samples at the
- modem sample rate given by freedv_get_n_max_modem_samples(). To
- account for difference in the transmit and receive sample clock
+ demod_in[] is a block of received samples sampled at 8000 Hz.
+ To account for difference in the transmit and receive sample clock
frequencies, the number of demod_in[] samples is time varying. You
MUST call freedv_nin() BEFORE each call to freedv_rx() and pass
exactly that many samples to this function.
freedv_get_n_speech_samples() and 0.
700 and 700B mode: The number of output speech samples returned will
- is always be freedv_get_n_speech_samples(), regardless of sync.
+ always be freedv_get_n_speech_samples(), regardless of sync.
The peak level of demod_in[] is not critical, as the demod works
well over a wide range of amplitude scaling. However avoid clipping
assert(f != NULL);
COMP rx_fdm[f->n_max_modem_samples];
int i;
+ int nin = freedv_nin(f);
- assert(f->nin <= f->n_max_modem_samples);
+ assert(nin <= f->n_max_modem_samples);
- for(i=0; i<f->nin; i++) {
+ for(i=0; i<nin; i++) {
rx_fdm[i].real = (float)demod_in[i];
rx_fdm[i].imag = 0.0;
}
assert(f != NULL);
COMP rx_fdm[f->n_max_modem_samples];
int i;
+ int nin = freedv_nin(f);
- assert(f->nin <= f->n_max_modem_samples);
+ assert(nin <= f->n_max_modem_samples);
- for(i=0; i<f->nin; i++) {
+ for(i=0; i<nin; i++) {
rx_fdm[i].real = demod_in[i];
rx_fdm[i].imag = 0;
}
assert(f->nin <= f->n_max_modem_samples);
- for(i=0; i<f->nin; i++)
- demod_in[i] = fcmult(1.0/FDMDV_SCALE, demod_in[i]);
-
bits_per_codec_frame = codec2_bits_per_frame(f->codec2);
bytes_per_codec_frame = (bits_per_codec_frame + 7) / 8;
nout = f->n_speech_samples;
if (f->mode == FREEDV_MODE_1600) {
+ for(i=0; i<f->nin; i++)
+ demod_in[i] = fcmult(1.0/FDMDV_SCALE, demod_in[i]);
+
bits_per_fdmdv_frame = fdmdv_bits_per_frame(f->fdmdv);
nin_prev = f->nin;
float rx_bits[COHPSK_BITS_PER_FRAME];
int sync;
+ i = quisk_cfInterpDecim(demod_in, freedv_nin(f), f->ptFilter8000to7500, 15, 16);
+ //if (i != f->nin)
+ // printf("freedv_comprx decimation: input %d output %d\n", freedv_nin(f), i);
+
+ for(i=0; i<f->nin; i++)
+ demod_in[i] = fcmult(1.0/FDMDV_SCALE, demod_in[i]);
+
nin_prev = f->nin;
- cohpsk_demod(f->cohpsk, rx_bits, &sync, demod_in, &f->nin);
+ cohpsk_demod(f->cohpsk, rx_bits, &sync, demod_in, &f->nin);
f->sync = sync;
cohpsk_get_demod_stats(f->cohpsk, &f->stats);
f->snr_est = f->stats.snr_est;
- if (sync) {
+ if (sync) {
if (f->test_frames == 0) {
data_flag_index = codec2_get_spare_bit_index(f->codec2);
\*---------------------------------------------------------------------------*/
// Set integers
-void freedv_set_test_frames (struct freedv *f, int val) {f->smooth_symbols = val;}
-void freedv_set_squelch_en (struct freedv *f, int val) {f->squelch_en = val;}
-void freedv_zero_total_bit_errors (struct freedv *f) {f->total_bit_errors = 0;}
+void freedv_set_test_frames (struct freedv *f, int val) {f->test_frames = val;}
+void freedv_set_squelch_en (struct freedv *f, int val) {f->squelch_en = val;}
+void freedv_zero_total_bit_errors (struct freedv *f) {f->total_bit_errors = 0;}
// Set floats
-void freedv_set_snr_squelch_thresh (struct freedv *f, float val) {f->snr_squelch_thresh = val;}
+void freedv_set_snr_squelch_thresh (struct freedv *f, float val) {f->snr_squelch_thresh = val;}
/*---------------------------------------------------------------------------*\
\*---------------------------------------------------------------------------*/
// Get integers
-int freedv_get_test_frames (struct freedv *f) {return f->test_frames;}
-int freedv_get_n_speech_samples (struct freedv *f) {return f->n_speech_samples;}
-int freedv_get_modem_sample_rate (struct freedv *f) {return f->modem_sample_rate;}
-int freedv_get_n_max_modem_samples (struct freedv *f) {return f->n_max_modem_samples;}
-int freedv_get_n_nom_modem_samples (struct freedv *f) {return f->n_nom_modem_samples;}
-int freedv_get_total_bits (struct freedv *f) {return f->total_bits;}
-int freedv_get_total_bit_errors (struct freedv *f) {return f->total_bit_errors;}
-int freedv_get_sync (struct freedv *f) {return f->stats.sync;}
+int freedv_get_test_frames (struct freedv *f) {return f->test_frames;}
+int freedv_get_n_speech_samples (struct freedv *f) {return f->n_speech_samples;}
+//int freedv_get_modem_sample_rate (struct freedv *f) {return f->modem_sample_rate;}
+int freedv_get_n_max_modem_samples (struct freedv *f) {return f->n_max_modem_samples;}
+int freedv_get_n_nom_modem_samples (struct freedv *f) {return f->n_nom_modem_samples;}
+int freedv_get_total_bits (struct freedv *f) {return f->total_bits;}
+int freedv_get_total_bit_errors (struct freedv *f) {return f->total_bit_errors;}
+int freedv_get_sync (struct freedv *f) {return f->stats.sync;}
// Get floats
+
+/*-- Functions below this line are private, and not meant for public use --*/
+/*---------------------------------------------------------------------------*\
+
+ FUNCTIONS...: quisk_filt_cfInit
+ AUTHOR......: Jim Ahlstrom
+ DATE CREATED: 27 August 2015
+
+ Initialize a FIR filter that will be used to change sample rates. These rate
+ changing filters were copied from Quisk and modified for float samples.
+
+\*---------------------------------------------------------------------------*/
+
+static void quisk_filt_cfInit(struct quisk_cfFilter * filter, float * coefs, int taps)
+{ // Prepare a new filter using coefs and taps. Samples are complex.
+ filter->dCoefs = coefs;
+ filter->cSamples = (COMP *)malloc(taps * sizeof(COMP));
+ memset(filter->cSamples, 0, taps * sizeof(COMP));
+ filter->ptcSamp = filter->cSamples;
+ filter->nTaps = taps;
+ filter->cBuf = NULL;
+ filter->nBuf = 0;
+ filter->decim_index = 0;
+}
+
+/*---------------------------------------------------------------------------*\
+
+ FUNCTIONS...: quisk_filt_destroy
+ AUTHOR......: Jim Ahlstrom
+ DATE CREATED: 27 August 2015
+
+ Destroy the FIR filter and free all resources.
+
+\*---------------------------------------------------------------------------*/
+
+static void quisk_filt_destroy(struct quisk_cfFilter * filter)
+{
+ if (filter->cSamples) {
+ free(filter->cSamples);
+ filter->cSamples = NULL;
+ }
+ if (filter->cBuf) {
+ free(filter->cBuf);
+ filter->cBuf = NULL;
+ }
+}
+
+/*---------------------------------------------------------------------------*\
+
+ FUNCTIONS...: quisk_cfInterpDecim
+ AUTHOR......: Jim Ahlstrom
+ DATE CREATED: 27 August 2015
+
+ Take an array of samples cSamples of length count, multiply the sample rate
+ by interp, and then divide the sample rate by decim. Return the new number
+ of samples. Each specific interp and decim will require its own custom
+ FIR filter.
+
+\*---------------------------------------------------------------------------*/
+
+static int quisk_cfInterpDecim(COMP * cSamples, int count, struct quisk_cfFilter * filter, int interp, int decim)
+{ // Interpolate by interp, and then decimate by decim.
+ // This uses the float coefficients of filter (not the complex). Samples are complex.
+ int i, k, nOut;
+ float * ptCoef;
+ COMP * ptSample;
+ COMP csample;
+
+ if (count > filter->nBuf) { // increase size of sample buffer
+ filter->nBuf = count * 2;
+ if (filter->cBuf)
+ free(filter->cBuf);
+ filter->cBuf = (COMP *)malloc(filter->nBuf * sizeof(COMP));
+ }
+ memcpy(filter->cBuf, cSamples, count * sizeof(COMP));
+ nOut = 0;
+ for (i = 0; i < count; i++) {
+ // Put samples into buffer left to right. Use samples right to left.
+ *filter->ptcSamp = filter->cBuf[i];
+ while (filter->decim_index < interp) {
+ ptSample = filter->ptcSamp;
+ ptCoef = filter->dCoefs + filter->decim_index;
+ csample.real = 0;
+ csample.imag = 0;
+ for (k = 0; k < filter->nTaps / interp; k++, ptCoef += interp) {
+ csample.real += (*ptSample).real * *ptCoef;
+ csample.imag += (*ptSample).imag * *ptCoef;
+ if (--ptSample < filter->cSamples)
+ ptSample = filter->cSamples + filter->nTaps - 1;
+ }
+ cSamples[nOut].real = csample.real * interp;
+ cSamples[nOut].imag = csample.imag * interp;
+ nOut++;
+ filter->decim_index += decim;
+ }
+ if (++filter->ptcSamp >= filter->cSamples + filter->nTaps)
+ filter->ptcSamp = filter->cSamples;
+ filter->decim_index = filter->decim_index - interp;
+ }
+ return nOut;
+}
+
#include "codec2_fdmdv.h"
#include "codec2_cohpsk.h"
+struct quisk_cfFilter { // Structure to hold the static data for FIR filters
+ float * dCoefs; // filter coefficients
+ int nBuf; // dimension of cBuf
+ int nTaps; // dimension of dSamples, cSamples, dCoefs
+ int decim_index; // index of next sample for decimation
+ COMP * cSamples; // storage for old samples
+ COMP * ptcSamp; // next available position in cSamples
+ COMP * cBuf; // auxillary buffer for interpolation
+} ;
+
+static int quisk_cfInterpDecim(COMP *, int, struct quisk_cfFilter *, int, int);
+static void quisk_filt_cfInit(struct quisk_cfFilter *, float *, int);
+static void quisk_filt_destroy(struct quisk_cfFilter *);
+static float quiskFilt120t480[480];
+
struct freedv {
int mode;
struct MODEM_STATS stats;
struct COHPSK *cohpsk;
+ struct quisk_cfFilter * ptFilter7500to8000; // Filters to change to/from 7500 and 8000 sps
+ struct quisk_cfFilter * ptFilter8000to7500;
+
int n_speech_samples;
int n_nom_modem_samples; // size of tx and most rx modem sample buffers
int n_max_modem_samples; // make your rx modem sample buffers this big
+ int n_nat_modem_samples; // tx modem sample block length as used by the modem before interpolation to output
int modem_sample_rate; // ATM caller is responsible for meeting this (TBC)
int clip; // non-zero for cohpsk modem output clipping for low PAPR
};
+// FIR filter suitable for changing rates 7500 to/from 8000
+// Sample 120000 Hz, pass 2700, stop 3730, ripple 0.1dB, atten 100 dB. Stop 0.03108.
+static float quiskFilt120t480[480] = { -0.000005050567303837, -0.000000267011791999, 0.000000197734700398, 0.000001038946634000,
+ 0.000002322193058869, 0.000004115682735322, 0.000006499942123311, 0.000009551098482930, 0.000013350669444763,
+ 0.000017966192635412, 0.000023463361155584, 0.000029885221425020, 0.000037271082107518, 0.000045630720487935,
+ 0.000054970017069384, 0.000065233162392019, 0.000076360900545177, 0.000088271373315159, 0.000100818605854714,
+ 0.000113853476544409, 0.000127174196746337, 0.000140558396336177, 0.000153744508371709, 0.000166450784469067,
+ 0.000178368313347299, 0.000189176709991702, 0.000198541881389953, 0.000206128795372885, 0.000211604878787747,
+ 0.000214655997661182, 0.000214994859281552, 0.000212358734245594, 0.000206539880117977, 0.000197379393194548,
+ 0.000184780318878738, 0.000168719942655099, 0.000149250512353807, 0.000126511346757621, 0.000100726393185629,
+ 0.000072210925236429, 0.000041365841965015, 0.000008680571408025, -0.000025277165852799, -0.000059865389594949,
+-0.000094384355854646, -0.000128080670195777, -0.000160170174848483, -0.000189854272533545, -0.000216333899003825,
+-0.000238836419299503, -0.000256632149501508, -0.000269058714331757, -0.000275541485292432, -0.000275614059005332,
+-0.000268937472718753, -0.000255317038867589, -0.000234717772155001, -0.000207273956099563, -0.000173297342436372,
+-0.000133280012107173, -0.000087895370243821, -0.000037986085678081, 0.000015440388211825, 0.000071232572821451,
+ 0.000128114399130489, 0.000184710477990398, 0.000239577162514028, 0.000291234779803098, 0.000338204791740229,
+ 0.000379047713684221, 0.000412403761615261, 0.000437031818051652, 0.000451848709179591, 0.000455966225408344,
+ 0.000448726371643413, 0.000429729020814434, 0.000398857326863837, 0.000356297600912998, 0.000302547334727027,
+ 0.000238422248479072, 0.000165048886226905, 0.000083853091464077, -0.000003462782744354, -0.000094949813106744,
+-0.000188451833293202, -0.000281651282503015, -0.000372121907291206, -0.000457387566635848, -0.000534985542936898,
+-0.000602532044011899, -0.000657788245032425, -0.000698728981427767, -0.000723604675185869, -0.000731002305621048,
+-0.000719899536922384, -0.000689709694056092, -0.000640319946685634, -0.000572115873292030, -0.000485996080304965,
+-0.000383371840261246, -0.000266155252511831, -0.000136731311264191, 0.000002082667095075, 0.000147092077716480,
+ 0.000294790953130229, 0.000441441918072383, 0.000583164190168290, 0.000716029226064227, 0.000836164238172957,
+ 0.000939856052624227, 0.001023657909064450, 0.001084492755093968, 0.001119751426837743, 0.001127383039339373,
+ 0.001105974243787613, 0.001054815583369999, 0.000973950761085690, 0.000864209315714227, 0.000727219011746881,
+ 0.000565398080608305, 0.000381924396468366, 0.000180685902835315, -0.000033793183292569, -0.000256444114966522,
+-0.000481764526566339, -0.000703946352348464, -0.000917016099829735, -0.001114986581270253, -0.001292014799874503,
+-0.001442563411804926, -0.001561559957317790, -0.001644551048567398, -0.001687846581475964, -0.001688649703502788,
+-0.001645167889846890, -0.001556702802350076, -0.001423714708648073, -0.001247857669697092, -0.001031986722557201,
+-0.000780131048444402, -0.000497436825078657, -0.000190077210351809, 0.000134868279325909, 0.000469563533327739,
+ 0.000805591531546815, 0.001134152328775355, 0.001446279849797673, 0.001733071409562941, 0.001985924997799762,
+ 0.002196778054604388, 0.002358342626407065, 0.002464328098407475, 0.002509648218888532, 0.002490604086803692,
+ 0.002405037734357425, 0.002252452724297770, 0.002034094661603120, 0.001752990365583534, 0.001413941154886139,
+ 0.001023470495638453, 0.000589723521647734, 0.000122320866350319, -0.000367832138027160, -0.000868777013398284,
+-0.001367771151677059, -0.001851587344265625, -0.002306838088978190, -0.002720317947026380, -0.003079353614002113,
+-0.003372155891804708, -0.003588162376578369, -0.003718362558663737, -0.003755596511143005, -0.003694818131674599,
+-0.003533315298404129, -0.003270878754553819, -0.002909914962857412, -0.002455496391464944, -0.001915346645364514,
+-0.001299757227227888, -0.000621437066532776, 0.000104706515738248, 0.000861849931067767, 0.001631595707499856,
+ 0.002394368911341672, 0.003129858565588139, 0.003817496679992245, 0.004436963307209760, 0.004968707287606522,
+ 0.005394469536085115, 0.005697797543539088, 0.005864537618023589, 0.005883292537600076, 0.005745832319314692,
+ 0.005447447099071761, 0.004987231255534477, 0.004368289529377007, 0.003597859022418248, 0.002687338851256991,
+ 0.001652226293162047, 0.000511956075882180, -0.000710356149138656, -0.001988263330091648, -0.003292424566049982,
+-0.004591123342747130, -0.005850857852106148, -0.007036991266043732, -0.008114450164977267, -0.009048456200082230,
+-0.009805276478965942, -0.010352975302354198, -0.010662152577592631, -0.010706650669328861, -0.010464214075017983,
+-0.009917087295446811, -0.009052534679222271, -0.007863270920348924, -0.006347789704693751, -0.004510582323649121,
+-0.002362238055733795, 0.000080576968834213, 0.002795265196543707, 0.005753566158586979, 0.008921944932552510,
+ 0.012262093950265378, 0.015731539846483594, 0.019284344624007944, 0.022871886384520687, 0.026443706729191677,
+ 0.029948406200633094, 0.033334570666910354, 0.036551709955124537, 0.039551189200810140, 0.042287133974308874,
+ 0.044717290029466283, 0.046803820535016104, 0.048514022996355009, 0.049820951883635139, 0.050703932928426454,
+ 0.051148959210315710, 0.051148959210315710, 0.050703932928426454, 0.049820951883635139, 0.048514022996355009,
+ 0.046803820535016104, 0.044717290029466283, 0.042287133974308874, 0.039551189200810140, 0.036551709955124537,
+ 0.033334570666910354, 0.029948406200633094, 0.026443706729191677, 0.022871886384520687, 0.019284344624007944,
+ 0.015731539846483594, 0.012262093950265378, 0.008921944932552510, 0.005753566158586979, 0.002795265196543707,
+ 0.000080576968834213, -0.002362238055733795, -0.004510582323649121, -0.006347789704693751, -0.007863270920348924,
+-0.009052534679222271, -0.009917087295446811, -0.010464214075017983, -0.010706650669328861, -0.010662152577592631,
+-0.010352975302354198, -0.009805276478965942, -0.009048456200082230, -0.008114450164977267, -0.007036991266043732,
+-0.005850857852106148, -0.004591123342747130, -0.003292424566049982, -0.001988263330091648, -0.000710356149138656,
+ 0.000511956075882180, 0.001652226293162047, 0.002687338851256991, 0.003597859022418248, 0.004368289529377007,
+ 0.004987231255534477, 0.005447447099071761, 0.005745832319314692, 0.005883292537600076, 0.005864537618023589,
+ 0.005697797543539088, 0.005394469536085115, 0.004968707287606522, 0.004436963307209760, 0.003817496679992245,
+ 0.003129858565588139, 0.002394368911341672, 0.001631595707499856, 0.000861849931067767, 0.000104706515738248,
+-0.000621437066532776, -0.001299757227227888, -0.001915346645364514, -0.002455496391464944, -0.002909914962857412,
+-0.003270878754553819, -0.003533315298404129, -0.003694818131674599, -0.003755596511143005, -0.003718362558663737,
+-0.003588162376578369, -0.003372155891804708, -0.003079353614002113, -0.002720317947026380, -0.002306838088978190,
+-0.001851587344265625, -0.001367771151677059, -0.000868777013398284, -0.000367832138027160, 0.000122320866350319,
+ 0.000589723521647734, 0.001023470495638453, 0.001413941154886139, 0.001752990365583534, 0.002034094661603120,
+ 0.002252452724297770, 0.002405037734357425, 0.002490604086803692, 0.002509648218888532, 0.002464328098407475,
+ 0.002358342626407065, 0.002196778054604388, 0.001985924997799762, 0.001733071409562941, 0.001446279849797673,
+ 0.001134152328775355, 0.000805591531546815, 0.000469563533327739, 0.000134868279325909, -0.000190077210351809,
+-0.000497436825078657, -0.000780131048444402, -0.001031986722557201, -0.001247857669697092, -0.001423714708648073,
+-0.001556702802350076, -0.001645167889846890, -0.001688649703502788, -0.001687846581475964, -0.001644551048567398,
+-0.001561559957317790, -0.001442563411804926, -0.001292014799874503, -0.001114986581270253, -0.000917016099829735,
+-0.000703946352348464, -0.000481764526566339, -0.000256444114966522, -0.000033793183292569, 0.000180685902835315,
+ 0.000381924396468366, 0.000565398080608305, 0.000727219011746881, 0.000864209315714227, 0.000973950761085690,
+ 0.001054815583369999, 0.001105974243787613, 0.001127383039339373, 0.001119751426837743, 0.001084492755093968,
+ 0.001023657909064450, 0.000939856052624227, 0.000836164238172957, 0.000716029226064227, 0.000583164190168290,
+ 0.000441441918072383, 0.000294790953130229, 0.000147092077716480, 0.000002082667095075, -0.000136731311264191,
+-0.000266155252511831, -0.000383371840261246, -0.000485996080304965, -0.000572115873292030, -0.000640319946685634,
+-0.000689709694056092, -0.000719899536922384, -0.000731002305621048, -0.000723604675185869, -0.000698728981427767,
+-0.000657788245032425, -0.000602532044011899, -0.000534985542936898, -0.000457387566635848, -0.000372121907291206,
+-0.000281651282503015, -0.000188451833293202, -0.000094949813106744, -0.000003462782744354, 0.000083853091464077,
+ 0.000165048886226905, 0.000238422248479072, 0.000302547334727027, 0.000356297600912998, 0.000398857326863837,
+ 0.000429729020814434, 0.000448726371643413, 0.000455966225408344, 0.000451848709179591, 0.000437031818051652,
+ 0.000412403761615261, 0.000379047713684221, 0.000338204791740229, 0.000291234779803098, 0.000239577162514028,
+ 0.000184710477990398, 0.000128114399130489, 0.000071232572821451, 0.000015440388211825, -0.000037986085678081,
+-0.000087895370243821, -0.000133280012107173, -0.000173297342436372, -0.000207273956099563, -0.000234717772155001,
+-0.000255317038867589, -0.000268937472718753, -0.000275614059005332, -0.000275541485292432, -0.000269058714331757,
+-0.000256632149501508, -0.000238836419299503, -0.000216333899003825, -0.000189854272533545, -0.000160170174848483,
+-0.000128080670195777, -0.000094384355854646, -0.000059865389594949, -0.000025277165852799, 0.000008680571408025,
+ 0.000041365841965015, 0.000072210925236429, 0.000100726393185629, 0.000126511346757621, 0.000149250512353807,
+ 0.000168719942655099, 0.000184780318878738, 0.000197379393194548, 0.000206539880117977, 0.000212358734245594,
+ 0.000214994859281552, 0.000214655997661182, 0.000211604878787747, 0.000206128795372885, 0.000198541881389953,
+ 0.000189176709991702, 0.000178368313347299, 0.000166450784469067, 0.000153744508371709, 0.000140558396336177,
+ 0.000127174196746337, 0.000113853476544409, 0.000100818605854714, 0.000088271373315159, 0.000076360900545177,
+ 0.000065233162392019, 0.000054970017069384, 0.000045630720487935, 0.000037271082107518, 0.000029885221425020,
+ 0.000023463361155584, 0.000017966192635412, 0.000013350669444763, 0.000009551098482930, 0.000006499942123311,
+ 0.000004115682735322, 0.000002322193058869, 0.000001038946634000, 0.000000197734700398, -0.000000267011791999,
+-0.000005050567303837 };
#endif
#ifdef __cplusplus