diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/CMakeLists.txt | 59 | ||||
-rw-r--r-- | src/fl2k_ampliphase.c (renamed from src/fl2k_iq.c) | 0 | ||||
-rw-r--r-- | src/fl2k_file.c | 215 | ||||
-rw-r--r-- | src/fl2k_fm.c | 621 | ||||
-rw-r--r-- | src/fl2k_garage.c | 545 | ||||
-rw-r--r-- | src/fl2k_tcp.c | 256 | ||||
-rw-r--r-- | src/fl2k_test.c | 308 | ||||
-rw-r--r-- | src/rds_mod.c | 302 | ||||
-rw-r--r-- | src/rds_waveforms.c | 153 |
9 files changed, 6 insertions, 2453 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 543226e..f4ec556 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -75,69 +75,22 @@ endif() ######################################################################## # Build utility ######################################################################## -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 rds_waveforms.c rds_mod.c) -add_executable(fl2k_garage fl2k_garage.c) -add_executable(fl2k_iq fl2k_iq.c) -set(INSTALL_TARGETS libosmo-fl2k_shared libosmo-fl2k_static fl2k_file fl2k_tcp fl2k_test fl2k_fm fl2k_garage fl2k_iq) +add_executable(fl2k_ampliphase fl2k_ampliphase.c) +set(INSTALL_TARGETS libosmo-fl2k_shared libosmo-fl2k_static fl2k_ampliphase) -target_link_libraries(fl2k_file libosmo-fl2k_shared - ${LIBUSB_LIBRARIES} - ${CMAKE_THREAD_LIBS_INIT} -) - -target_link_libraries(fl2k_tcp libosmo-fl2k_shared - ${LIBUSB_LIBRARIES} - ${CMAKE_THREAD_LIBS_INIT} -) - -target_link_libraries(fl2k_test libosmo-fl2k_shared - ${LIBUSB_LIBRARIES} - ${CMAKE_THREAD_LIBS_INIT} -) - -target_link_libraries(fl2k_fm libosmo-fl2k_shared - ${LIBUSB_LIBRARIES} - ${CMAKE_THREAD_LIBS_INIT} -) - -target_link_libraries(fl2k_garage libosmo-fl2k_shared - ${LIBUSB_LIBRARIES} - ${CMAKE_THREAD_LIBS_INIT} -) - -target_link_libraries(fl2k_iq libosmo-fl2k_shared +target_link_libraries(fl2k_ampliphase libosmo-fl2k_shared ${LIBUSB_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ) if(UNIX) -target_link_libraries(fl2k_test m) -target_link_libraries(fl2k_fm m) -target_link_libraries(fl2k_garage m) -target_link_libraries(fl2k_iq m) +target_link_libraries(fl2k_ampliphase m) endif() if(WIN32 AND NOT MINGW) -target_link_libraries(fl2k_file libgetopt_static) -target_link_libraries(fl2k_tcp ws2_32 libgetopt_static) -target_link_libraries(fl2k_test libgetopt_static) -target_link_libraries(fl2k_fm libgetopt_static) -target_link_libraries(fl2k_garage libgetopt_static) -target_link_libraries(fl2k_iq libgetopt_static) -set_property(TARGET fl2k_file APPEND PROPERTY COMPILE_DEFINITIONS "libosmo-fl2k_STATIC" ) -set_property(TARGET fl2k_tcp APPEND PROPERTY COMPILE_DEFINITIONS "libosmo-fl2k_STATIC" ) -set_property(TARGET fl2k_test APPEND PROPERTY COMPILE_DEFINITIONS "libosmo-fl2k_STATIC" ) -set_property(TARGET fl2k_fm APPEND PROPERTY COMPILE_DEFINITIONS "libosmo-fl2k_STATIC" ) -set_property(TARGET fl2k_garage APPEND PROPERTY COMPILE_DEFINITIONS "libosmo-fl2k_STATIC" ) -set_property(TARGET fl2k_iq APPEND PROPERTY COMPILE_DEFINITIONS "libosmo-fl2k_STATIC" ) -endif() - -if(MINGW) -target_link_libraries(fl2k_tcp ws2_32) +target_link_libraries(fl2k_ampliphase libgetopt_static) +set_property(TARGET fl2k_ampliphase APPEND PROPERTY COMPILE_DEFINITIONS "libosmo-fl2k_STATIC" ) endif() ######################################################################## diff --git a/src/fl2k_iq.c b/src/fl2k_ampliphase.c index db8d7bf..db8d7bf 100644 --- a/src/fl2k_iq.c +++ b/src/fl2k_ampliphase.c diff --git a/src/fl2k_file.c b/src/fl2k_file.c deleted file mode 100644 index 5ae199b..0000000 --- a/src/fl2k_file.c +++ /dev/null @@ -1,215 +0,0 @@ -/* - * osmo-fl2k, turns FL2000-based USB 3.0 to VGA adapters into - * low cost DACs - * - * Copyright (C) 2016-2018 by Steve Markgraf <steve@steve-m.de> - * - * SPDX-License-Identifier: GPL-2.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 2 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 <errno.h> -#include <signal.h> -#include <string.h> -#include <stdio.h> -#include <stdlib.h> - -#ifndef _WIN32 -#include <unistd.h> -#define sleep_ms(ms) usleep(ms*1000) -#else -#include <windows.h> -#include <io.h> -#include <fcntl.h> -#include "getopt/getopt.h" -#define sleep_ms(ms) Sleep(ms) -#endif - -#include "osmo-fl2k.h" - -static fl2k_dev_t *dev = NULL; - -static volatile int do_exit = 0; -static volatile int repeat = 1; -FILE *file; -char *txbuf = NULL; - -void usage(void) -{ - fprintf(stderr, - "fl2k_file, a sample player for FL2K VGA dongles\n\n" - "Usage:\n" - "\t[-d device_index (default: 0)]\n" - "\t[-r repeat file (default: 1)]\n" - "\t[-s samplerate (default: 100 MS/s)]\n" - "\tfilename (use '-' to read from stdin)\n\n" - ); - exit(1); -} - -#ifdef _WIN32 -BOOL WINAPI -sighandler(int signum) -{ - if (CTRL_C_EVENT == signum) { - fprintf(stderr, "Signal caught, exiting!\n"); - fl2k_stop_tx(dev); - do_exit = 1; - return TRUE; - } - return FALSE; -} -#else -static void sighandler(int signum) -{ - fprintf(stderr, "Signal caught, exiting!\n"); - fl2k_stop_tx(dev); - do_exit = 1; -} -#endif - -void fl2k_callback(fl2k_data_info_t *data_info) -{ - int r, left = FL2K_BUF_LEN; - static uint32_t repeat_cnt = 0; - - if (data_info->device_error) { - fprintf(stderr, "Device error, exiting.\n"); - do_exit = 1; - return; - } - - data_info->sampletype_signed = 1; - data_info->r_buf = txbuf; - - while (!do_exit && (left > 0)) { - r = fread(txbuf + (FL2K_BUF_LEN - left), 1, left, file); - - if (ferror(file)) - fprintf(stderr, "File Error\n"); - - if (feof(file)) { - if (repeat && (r > 0)) { - repeat_cnt++; - fprintf(stderr, "repeat %d\n", repeat_cnt); - rewind(file); - } else { - fl2k_stop_tx(dev); - do_exit = 1; - } - } - - if (r > 0) - left -= r; - } -} - -int main(int argc, char **argv) -{ -#ifndef _WIN32 - struct sigaction sigact, sigign; -#endif - int r, opt, i; - uint32_t samp_rate = 100000000; - uint32_t buf_num = 0; - int dev_index = 0; - void *status; - char *filename = NULL; - - while ((opt = getopt(argc, argv, "d:r:s:")) != -1) { - switch (opt) { - case 'd': - dev_index = (uint32_t)atoi(optarg); - break; - case 'r': - repeat = (int)atoi(optarg); - break; - case 's': - samp_rate = (uint32_t)atof(optarg); - break; - default: - usage(); - break; - } - } - - if (argc <= optind) - usage(); - else - filename = argv[optind]; - - if (dev_index < 0) - exit(1); - - if (strcmp(filename, "-") == 0) { /* Read samples from stdin */ - file = stdin; -#ifdef _WIN32 - _setmode(_fileno(stdin), _O_BINARY); -#endif - } else { - file = fopen(filename, "rb"); - if (!file) { - fprintf(stderr, "Failed to open %s\n", filename); - return -ENOENT; - } - } - - txbuf = malloc(FL2K_BUF_LEN); - if (!txbuf) { - fprintf(stderr, "malloc error!\n"); - goto out; - } - - fl2k_open(&dev, (uint32_t)dev_index); - if (NULL == dev) { - fprintf(stderr, "Failed to open fl2k device #%d.\n", dev_index); - goto out; - } - - r = fl2k_start_tx(dev, fl2k_callback, NULL, 0); - - /* Set the sample rate */ - r = fl2k_set_sample_rate(dev, samp_rate); - if (r < 0) - fprintf(stderr, "WARNING: Failed to set sample rate.\n"); - - -#ifndef _WIN32 - sigact.sa_handler = sighandler; - sigemptyset(&sigact.sa_mask); - sigact.sa_flags = 0; - sigign.sa_handler = SIG_IGN; - sigaction(SIGINT, &sigact, NULL); - sigaction(SIGTERM, &sigact, NULL); - sigaction(SIGQUIT, &sigact, NULL); - sigaction(SIGPIPE, &sigign, NULL); -#else - SetConsoleCtrlHandler( (PHANDLER_ROUTINE) sighandler, TRUE ); -#endif - - while (!do_exit) - sleep_ms(500); - - fl2k_close(dev); - -out: - if (txbuf) - free(txbuf); - - if (file && (file != stdin)) - fclose(file); - - return 0; -} diff --git a/src/fl2k_fm.c b/src/fl2k_fm.c deleted file mode 100644 index b22efcd..0000000 --- a/src/fl2k_fm.c +++ /dev/null @@ -1,621 +0,0 @@ -/* - * osmo-fl2k, turns FL2000-based USB 3.0 to VGA adapters into - * low cost DACs - * - * Copyright (C) 2016-2018 by Steve Markgraf <steve@steve-m.de> - * - * based on FM modulator code from VGASIG: - * Copyright (C) 2009 by Bartek Kania <mbk@gnarf.org> - * - * SPDX-License-Identifier: GPL-2.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 2 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 <stdio.h> -#include <stdlib.h> -#include <signal.h> -#include <string.h> -#include <errno.h> - -#ifndef _WIN32 -#include <unistd.h> -#include <fcntl.h> -#include <getopt.h> -#else -#include <windows.h> -#include <io.h> -#include <fcntl.h> -#include "getopt/getopt.h" -#endif - -#include <math.h> -#include <pthread.h> - -#include "osmo-fl2k.h" -#include "rds_mod.h" - -#define BUFFER_SAMPLES_SHIFT 16 -#define BUFFER_SAMPLES (1 << BUFFER_SAMPLES_SHIFT) -#define BUFFER_SAMPLES_MASK ((1 << BUFFER_SAMPLES_SHIFT)-1) - -#define AUDIO_BUF_SIZE 1024 - -fl2k_dev_t *dev = NULL; -int do_exit = 0; - -pthread_t fm_thread; -pthread_mutex_t cb_mutex; -pthread_mutex_t fm_mutex; -pthread_cond_t cb_cond; -pthread_cond_t fm_cond; - -FILE *file; -int8_t *txbuf = NULL; -int8_t *fmbuf = NULL; -int8_t *buf1 = NULL; -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 deviation = 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; -int writepos, readpos; - -void usage(void) -{ - fprintf(stderr, - "fl2k_fm, an FM modulator for FL2K VGA dongles\n\n" - "Usage:" - "\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 for mono FM)]\n" - "\t[-s samplerate in Hz (default: 100 MS/s)]\n" - "\t[--rds (enables RDS, forces audio sample rate to 228 kHz)]\n" - "\t[--stereo (enables stereo, requires audio sample rate >= 114 kHz)]\n" - "\tfilename (use '-' to read from stdin)\n\n" - ); - exit(1); -} - -#ifdef _WIN32 -BOOL WINAPI -sighandler(int signum) -{ - if (CTRL_C_EVENT == signum) { - fprintf(stderr, "Signal caught, exiting!\n"); - fl2k_stop_tx(dev); - do_exit = 1; - pthread_cond_signal(&fm_cond); - return TRUE; - } - return FALSE; -} -#else -static void sighandler(int signum) -{ - fprintf(stderr, "Signal caught, exiting!\n"); - fl2k_stop_tx(dev); - do_exit = 1; - pthread_cond_signal(&fm_cond); -} -#endif - -/* DDS Functions */ - -#ifndef M_PI -# define M_PI 3.14159265358979323846 /* pi */ -# define M_PI_2 1.57079632679489661923 /* pi/2 */ -# define M_PI_4 0.78539816339744830962 /* pi/4 */ -# define M_1_PI 0.31830988618379067154 /* 1/pi */ -# define M_2_PI 0.63661977236758134308 /* 2/pi */ -#endif -#define DDS_2PI (M_PI * 2) /* 2 * Pi */ -#define DDS_3PI2 (M_PI_2 * 3) /* 3/2 * pi */ - -#define SIN_TABLE_ORDER 8 -#define SIN_TABLE_SHIFT (32 - SIN_TABLE_ORDER) -#define SIN_TABLE_LEN (1 << SIN_TABLE_ORDER) -#define ANG_INCR (0xffffffff / DDS_2PI) - -int8_t sine_table[SIN_TABLE_LEN]; -int sine_table_init = 0; - -typedef struct { - double sample_freq; - double freq; - double fslope; - unsigned long int phase; - unsigned long int phase_step; - unsigned long int phase_slope; -} dds_t; - -static inline void dds_setphase(dds_t *dds, double phase) -{ - dds->phase = phase * ANG_INCR; -} - -static inline double dds_getphase(dds_t *dds) -{ - return dds->phase / ANG_INCR; -} - -static inline void dds_set_freq(dds_t *dds, double freq, double fslope) -{ - dds->fslope = fslope; - dds->phase_step = (freq / dds->sample_freq) * 2 * M_PI * ANG_INCR; - - /* The slope parameter is used with the FM modulator to create - * a simple but very fast and effective interpolation filter. - * See the fm modulator for details */ - dds->freq = freq; - dds->phase_slope = (fslope / dds->sample_freq) * 2 * M_PI * ANG_INCR; -} - -dds_t dds_init(double sample_freq, double freq, double phase) -{ - dds_t dds; - int i; - - dds.sample_freq = sample_freq; - dds.phase = phase * ANG_INCR; - dds_set_freq(&dds, freq, 0); - - /* Initialize sine table, prescaled for 8 bit signed integer */ - if (!sine_table_init) { - double incr = 1.0 / (double)SIN_TABLE_LEN; - for (i = 0; i < SIN_TABLE_LEN; i++) - sine_table[i] = sin(incr * i * DDS_2PI) * 127; - - sine_table_init = 1; - } - - return dds; -} - -static inline int8_t dds_real(dds_t *dds) -{ - int tmp; - - tmp = dds->phase >> SIN_TABLE_SHIFT; - dds->phase += dds->phase_step; - dds->phase &= 0xffffffff; - - dds->phase_step += dds->phase_slope; - - return sine_table[tmp]; -} - -static inline void dds_real_buf(dds_t *dds, int8_t *buf, int count) -{ - int i; - for (i = 0; i < count; i++) - buf[i] = dds_real(dds); -} - -/* Signal generation and some helpers */ - -/* Generate the radio signal using the pre-calculated frequency information - * in the freq buffer */ -static void *fm_worker(void *arg) -{ - register double freq; - register double tmp; - dds_t carrier; - int8_t *tmp_ptr; - uint32_t len = 0; - uint32_t readlen, remaining; - int buf_prefilled = 0; - - /* Prepare the oscillators */ - carrier = dds_init(samp_rate, carrier_freq, 0); - - while (!do_exit) { - dds_set_freq(&carrier, freqbuf[readpos], slopebuf[readpos]); - readpos++; - readpos &= BUFFER_SAMPLES_MASK; - - /* check if we reach the end of the buffer */ - if ((len + carrier_per_signal) > FL2K_BUF_LEN) { - readlen = FL2K_BUF_LEN - len; - remaining = carrier_per_signal - readlen; - dds_real_buf(&carrier, &fmbuf[len], readlen); - - if (buf_prefilled) { - /* swap buffers */ - tmp_ptr = fmbuf; - fmbuf = txbuf; - txbuf = tmp_ptr; - pthread_cond_wait(&cb_cond, &cb_mutex); - } - - dds_real_buf(&carrier, fmbuf, remaining); - len = remaining; - - buf_prefilled = 1; - } else { - dds_real_buf(&carrier, &fmbuf[len], carrier_per_signal); - len += carrier_per_signal; - } - - pthread_cond_signal(&fm_cond); - } - - pthread_exit(NULL); -} - -static inline int writelen(int maxlen) -{ - int rp = readpos; - int len; - int r; - - if (rp < writepos) - rp += BUFFER_SAMPLES; - - len = rp - writepos; - - r = len > maxlen ? maxlen : len; - - return r; -} - -static inline double modulate_sample(int lastwritepos, double lastfreq, double sample) -{ - double freq, slope; - - /* Calculate modulator frequency at this point to lessen - * the calculations needed in the signal generator */ - freq = sample * deviation; - freq += carrier_freq; - - /* What we do here is calculate a linear "slope" from - the previous sample to this one. This is then used by - the modulator to gently increase/decrease the frequency - with each sample without the need to recalculate - the dds parameters. In fact this gives us a very - efficient and pretty good interpolation filter. */ - slope = freq - lastfreq; - slope /= carrier_per_signal; - slopebuf[lastwritepos] = slope; - freqbuf[writepos] = freq; - - return freq; -} - -void fm_modulator_mono(int use_rds) -{ - unsigned int i; - size_t len; - double freq; - double lastfreq = carrier_freq; - 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); - if (len > 1) { - len = fread(audio_buf, 2, len, file); - - 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, 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; - } - } else { - pthread_cond_wait(&fm_cond, &fm_mutex); - } - } -} - -void fl2k_callback(fl2k_data_info_t *data_info) -{ - if (data_info->device_error) { - fprintf(stderr, "Device error, exiting.\n"); - do_exit = 1; - pthread_cond_signal(&fm_cond); - } - - pthread_cond_signal(&cb_cond); - - data_info->sampletype_signed = 1; - data_info->r_buf = (char *)txbuf; -} - -int main(int argc, char **argv) -{ - int r, opt; - uint32_t buf_num = 0; - 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 - - 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; - case 'c': - carrier_freq = (uint32_t)atof(optarg); - break; - case 'f': - deviation = (uint32_t)atof(optarg); - break; - case 'i': - input_freq = (uint32_t)atof(optarg); - input_freq_specified = 1; - break; - case 's': - samp_rate = (uint32_t)atof(optarg); - break; - default: - usage(); - break; - } - } - - if (argc <= optind) { - usage(); - } else { - filename = argv[optind]; - } - - if (dev_index < 0) { - exit(1); - } - - 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); -#endif - } else { - file = fopen(filename, "rb"); - if (!file) { - fprintf(stderr, "Failed to open %s\n", filename); - return -ENOENT; - } - } - - /* allocate buffer */ - buf1 = malloc(FL2K_BUF_LEN); - buf2 = malloc(FL2K_BUF_LEN); - if (!buf1 || !buf2) { - fprintf(stderr, "malloc error!\n"); - exit(1); - } - - fmbuf = buf1; - txbuf = buf2; - - /* Decoded audio */ - freqbuf = malloc(BUFFER_SAMPLES * sizeof(double)); - slopebuf = malloc(BUFFER_SAMPLES * sizeof(double)); - readpos = 0; - writepos = 1; - - fprintf(stderr, "Samplerate:\t%3.2f MHz\n", (double)samp_rate/1000000); - fprintf(stderr, "Carrier:\t%3.2f MHz\n", (double)carrier_freq/1000000); - fprintf(stderr, "Frequencies:\t%3.2f MHz, %3.2f MHz\n", - (double)((samp_rate - carrier_freq) / 1000000.0), - (double)((samp_rate + carrier_freq) / 1000000.0)); - - pthread_mutex_init(&cb_mutex, NULL); - pthread_mutex_init(&fm_mutex, NULL); - pthread_cond_init(&cb_cond, NULL); - pthread_cond_init(&fm_cond, NULL); - pthread_attr_init(&attr); - - fl2k_open(&dev, (uint32_t)dev_index); - if (NULL == dev) { - fprintf(stderr, "Failed to open fl2k device #%d.\n", dev_index); - goto out; - } - - r = pthread_create(&fm_thread, &attr, fm_worker, NULL); - if (r < 0) { - fprintf(stderr, "Error spawning FM worker thread!\n"); - goto out; - } - - pthread_attr_destroy(&attr); - r = fl2k_start_tx(dev, fl2k_callback, NULL, 0); - - /* Set the sample rate */ - r = fl2k_set_sample_rate(dev, samp_rate); - if (r < 0) - fprintf(stderr, "WARNING: Failed to set sample rate. %d\n", r); - - /* read back actual frequency */ - samp_rate = fl2k_get_sample_rate(dev); - - /* Calculate needed constants */ - carrier_per_signal = samp_rate / input_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; - sigemptyset(&sigact.sa_mask); - sigact.sa_flags = 0; - sigign.sa_handler = SIG_IGN; - sigaction(SIGINT, &sigact, NULL); - sigaction(SIGTERM, &sigact, NULL); - sigaction(SIGQUIT, &sigact, NULL); - sigaction(SIGPIPE, &sigign, NULL); -#else - SetConsoleCtrlHandler( (PHANDLER_ROUTINE) sighandler, TRUE ); -#endif - - 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); - - if (file != stdin) - fclose(file); - - free(freqbuf); - free(slopebuf); - free(buf1); - free(buf2); - - return 0; -} diff --git a/src/fl2k_garage.c b/src/fl2k_garage.c deleted file mode 100644 index d50922e..0000000 --- a/src/fl2k_garage.c +++ /dev/null @@ -1,545 +0,0 @@ -/* - * osmo-fl2k, turns FL2000-based USB 3.0 to VGA adapters into - * low cost DACs - * - * Copyright (C) 2019 by Felix Erckenbrecht <eligs@eligs.de> - * - * based on fl2k_fm code by: - * Copyright (C) 2016-2018 by Steve Markgraf <steve@steve-m.de> - * - * based on FM modulator code from VGASIG: - * Copyright (C) 2009 by Bartek Kania <mbk@gnarf.org> - * - * SPDX-License-Identifier: GPL-2.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 2 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 <stdio.h> -#include <stdlib.h> -#include <signal.h> -#include <string.h> -#include <errno.h> -#include <stdint.h> - -#ifndef _WIN32 -#include <unistd.h> -#include <fcntl.h> -#include <getopt.h> -#else -#include <windows.h> -#include <io.h> -#include <fcntl.h> -#include "getopt/getopt.h" -#endif - -#include <math.h> -#include <pthread.h> - -#include "osmo-fl2k.h" -#include "rds_mod.h" - -#define BUFFER_SAMPLES_SHIFT 16 -#define BUFFER_SAMPLES (1 << BUFFER_SAMPLES_SHIFT) -#define BUFFER_SAMPLES_MASK ((1 << BUFFER_SAMPLES_SHIFT)-1) - -#define AUDIO_BUF_SIZE 4096 - -#define BASEBAND_SAMPLES_PER_CHIP 3 -#define BASEBAND_WORD_BITS 12 -#define BASEBAND_CHIPS_PER_BIT 3 -#define BASEBAND_CHIPS_PER_WORD (BASEBAND_WORD_BITS * BASEBAND_CHIPS_PER_BIT) -#define BASEBAND_CHIPS_PER_SPACE BASEBAND_CHIPS_PER_WORD -#define BASEBAND_SPACE_HIGH_CHIPS 1 -#define BASEBAND_SPACE_LOW_CHIPS (BASEBAND_CHIPS_PER_SPACE - BASEBAND_SPACE_HIGH_CHIPS) -#define BASEBAND_CHIPS_TOTAL (BASEBAND_CHIPS_PER_SPACE + BASEBAND_CHIPS_PER_WORD) - -fl2k_dev_t *dev = NULL; -int do_exit = 0; - -pthread_t am_thread; -pthread_mutex_t cb_mutex; -pthread_mutex_t am_mutex; -pthread_cond_t cb_cond; -pthread_cond_t am_cond; - -int16_t *sample_buf; -int sample_buf_size; - -int8_t *txbuf = NULL; -int8_t *ambuf = NULL; -int8_t *buf1 = NULL; -int8_t *buf2 = NULL; - -uint32_t samp_rate = 100000000; - -double mod_index = 0.9; -int carrier_freq = 40685000; -int carrier_per_signal; - -double *ampbuf; -double *slopebuf; -int writepos, readpos; - -void usage(void) -{ - fprintf(stderr, - "fl2k_garage, a garage door opener for FL2K VGA dongles\n\n" - "Usage:" - "\t[-d device index (default: 0)]\n" - "\t[-f carrier frequency (default: 40.685 MHz)]\n" - "\t[-c garage door code (12 Bit)]\n" - "\t[-b chip period in us (default 320 us)]\n" - "\t[-s samplerate in Hz (default: 100 MS/s)]\n" - ); - exit(1); -} - -#ifdef _WIN32 -BOOL WINAPI -sighandler(int signum) -{ - if (CTRL_C_EVENT == signum) { - fprintf(stderr, "Signal caught, exiting!\n"); - fl2k_stop_tx(dev); - do_exit = 1; - pthread_cond_signal(&am_cond); - return TRUE; - } - return FALSE; -} -#else -static void sighandler(int signum) -{ - fprintf(stderr, "Signal caught, exiting!\n"); - fl2k_stop_tx(dev); - do_exit = 1; - pthread_cond_signal(&am_cond); -} -#endif - -/* DDS Functions */ - -#ifndef M_PI -# define M_PI 3.14159265358979323846 /* pi */ -# define M_PI_2 1.57079632679489661923 /* pi/2 */ -# define M_PI_4 0.78539816339744830962 /* pi/4 */ -# define M_1_PI 0.31830988618379067154 /* 1/pi */ -# define M_2_PI 0.63661977236758134308 /* 2/pi */ -#endif -#define DDS_2PI (M_PI * 2) /* 2 * Pi */ -#define DDS_3PI2 (M_PI_2 * 3) /* 3/2 * pi */ - -#define SIN_TABLE_ORDER 8 -#define SIN_TABLE_SHIFT (32 - SIN_TABLE_ORDER) -#define SIN_TABLE_LEN (1 << SIN_TABLE_ORDER) -#define ANG_INCR (0xffffffff / DDS_2PI) - -int16_t sine_table[SIN_TABLE_LEN]; -int sine_table_init = 0; - -typedef struct { - double sample_freq; - double freq; - unsigned long int phase; - unsigned long int phase_step; - double amplitude; - double ampslope; -} dds_t; - -static inline void dds_set_freq(dds_t *dds, double freq) -{ - dds->freq = freq; - dds->phase_step = (freq / dds->sample_freq) * 2 * M_PI * ANG_INCR; -} - -static inline void dds_set_amp(dds_t *dds, double amplitude, double ampslope) -{ - dds->amplitude = amplitude; - dds->ampslope = ampslope; -} - -dds_t dds_init(double sample_freq, double freq, double phase, double amp) -{ - dds_t dds; - int i; - - dds.sample_freq = sample_freq; - dds.phase = phase * ANG_INCR; - dds_set_freq(&dds, freq); - dds_set_amp(&dds, amp, 0); - /* Initialize sine table, prescaled for 16 bit signed integer */ - if (!sine_table_init) { - double incr = 1.0 / (double)SIN_TABLE_LEN; - for (i = 0; i < SIN_TABLE_LEN; i++) - sine_table[i] = sin(incr * i * DDS_2PI) * 32767; - - sine_table_init = 1; - } - - return dds; -} - -static inline int8_t dds_real(dds_t *dds) -{ - int tmp; - int32_t amp; - - // advance dds generator - tmp = dds->phase >> SIN_TABLE_SHIFT; - dds->phase += dds->phase_step; - dds->phase &= 0xffffffff; - - amp = (int32_t)(dds->amplitude * 255) * sine_table[tmp]; - dds->amplitude += dds->ampslope; - - return (int8_t)(amp >> 16) ; -} - -static inline void dds_real_buf(dds_t *dds, int8_t *buf, int count) -{ - int i; - for (i = 0; i < count; i++) - buf[i] = dds_real(dds); -} - -/* Signal generation and some helpers */ - -/* Generate the radio signal using the pre-calculated amplitude information - * in the amp buffer */ -static void *am_worker(void *arg) -{ - register double freq; - register double tmp; - dds_t carrier; - int8_t *tmp_ptr; - uint32_t len = 0; - uint32_t readlen, remaining; - int buf_prefilled = 0; - - /* Prepare the oscillators */ - carrier = dds_init(samp_rate, carrier_freq, 1, 0); - - while (!do_exit) { - dds_set_amp(&carrier, ampbuf[readpos], slopebuf[readpos]); - readpos++; - readpos &= BUFFER_SAMPLES_MASK; - - /* check if we reach the end of the buffer */ - if ((len + carrier_per_signal) > FL2K_BUF_LEN) { - readlen = FL2K_BUF_LEN - len; - remaining = carrier_per_signal - readlen; - dds_real_buf(&carrier, &ambuf[len], readlen); - - if (buf_prefilled) { - /* swap buffers */ - tmp_ptr = ambuf; - ambuf = txbuf; - txbuf = tmp_ptr; - pthread_cond_wait(&cb_cond, &cb_mutex); - } - - dds_real_buf(&carrier, ambuf, remaining); - len = remaining; - - buf_prefilled = 1; - } else { - dds_real_buf(&carrier, &ambuf[len], carrier_per_signal); - len += carrier_per_signal; - } - - pthread_cond_signal(&am_cond); - } - - pthread_exit(NULL); -} - -static inline int writelen(int maxlen) -{ - int rp = readpos; - int len; - int r; - - if (rp < writepos) - rp += BUFFER_SAMPLES; - - len = rp - writepos; - - r = len > maxlen ? maxlen : len; - - return r; -} - -static inline int32_t modulate_sample_am(int lastwritepos, double lastamp, int16_t sample) -{ - double amp, slope; - - /* Calculate modulator amplitude at this point to lessen - * the calculations needed in the signal generator */ - amp = 1 - ((double)sample * mod_index); - - /* What we do here is calculate a linear "slope" from - the previous sample to this one. This is then used by - the modulator to gently increase/decrease the amplitude - with each sample without the need to recalculate - the dds parameters. In fact this gives us a very - efficient and pretty good interpolation filter. */ - slope = amp - lastamp; - slope /= carrier_per_signal; - slopebuf[lastwritepos] = slope; - ampbuf[writepos] = amp; - - return amp; -} - -void am_modulator(const int code_input) -{ - int counter = 0; - int code; - unsigned int i; - unsigned int b = 0; - size_t len; - int32_t lastamp = 0; - uint32_t lastwritepos = writepos; - int16_t sample = 0; - int samplebuf_pos = 0; - - /* - * 3*640 us = 1,92 ms pro Symbol - * 12 Symbole Daten (12 Bit) - * 11 Symbole Pause, 1 Symbol 1 (synch) - * - * 1 = __- - * 0 = _-- - */ - while (!do_exit) { - len = writelen(AUDIO_BUF_SIZE); - if (len > 1) { - if (len == 0) - do_exit = 1; - - for (i = 0; i < len; i++) { - /* Modulate and buffer the sample */ - sample = sample_buf[samplebuf_pos++]; - if(samplebuf_pos >= BASEBAND_SAMPLES_PER_CHIP * BASEBAND_CHIPS_TOTAL){ - samplebuf_pos = 0; - } - lastamp = modulate_sample_am(lastwritepos, lastamp, sample); - lastwritepos = writepos++; - writepos %= BUFFER_SAMPLES; - } - } else { - pthread_cond_wait(&am_cond, &am_mutex); - } - } -} - -void prepare_baseband(const int code_input, int16_t * sbuf){ - int counter; - int b; - int sample_no; - int16_t sample; - int msb_first_code; - - msb_first_code = 0; - // change to msb first and invert - for(b = 0;b<12;b++){ - msb_first_code <<= 1; - msb_first_code |= code_input & (1<<b) ? 0 : 1; - } - - sample_no = 0; - for(counter=0;counter < (BASEBAND_CHIPS_PER_SPACE + BASEBAND_CHIPS_PER_WORD) ; counter++){ - for(b=0 ; b<BASEBAND_SAMPLES_PER_CHIP ; b++){ - if(counter < (BASEBAND_SPACE_LOW_CHIPS)){ - sample = 0; - } - else if(counter < (BASEBAND_CHIPS_PER_SPACE)){ - // synch symbol - sample = 1; - } - else{ - int m; - m = counter % BASEBAND_CHIPS_PER_BIT; - if(m == 0){ - sample = 0; - } - else if(m == 1){ - sample = (msb_first_code & 1); - } - else{ - sample = 1; - if(b == BASEBAND_SAMPLES_PER_CHIP-1){ - msb_first_code >>= 1; - } - } - } - sbuf[counter * BASEBAND_SAMPLES_PER_CHIP + b] = sample; - } - } -} - -void fl2k_callback(fl2k_data_info_t *data_info) -{ - if (data_info->device_error) { - fprintf(stderr, "Device error, exiting.\n"); - do_exit = 1; - pthread_cond_signal(&am_cond); - } - - pthread_cond_signal(&cb_cond); - - data_info->sampletype_signed = 1; - data_info->r_buf = (char *)txbuf; -} - -int main(int argc, char **argv) -{ - int r, opt; - uint32_t buf_num = 0; - int dev_index = 0; - pthread_attr_t attr; - char *filename = NULL; - int option_index = 0; - int code = 0; - int chiptime_us = 320; - -#ifndef _WIN32 - struct sigaction sigact, sigign; -#endif - - static struct option long_options[] = - { - {0, 0, 0, 0} - }; - - while (1) { - opt = getopt_long(argc, argv, "b:c:f:m:s:", long_options, &option_index); - /* end of options reached */ - if (opt == -1) - break; - - switch (opt) { - case 0: - break; - case 'b': - chiptime_us = atoi(optarg); - break; - case 'c': - code = atoi(optarg); - code &= 4095; - break; - case 'f': - carrier_freq = (uint32_t)atof(optarg); - break; - case 'm': - mod_index = atof(optarg); - break; - case 's': - samp_rate = (uint32_t)atof(optarg); - break; - default: - usage(); - break; - } - } - - if (argc < optind) { - usage(); - } - - /* allocate buffer */ - buf1 = malloc(FL2K_BUF_LEN); - buf2 = malloc(FL2K_BUF_LEN); - if (!buf1 || !buf2) { - fprintf(stderr, "malloc error!\n"); - exit(1); - } - - ambuf = buf1; - txbuf = buf2; - - /* Decoded audio */ - slopebuf = malloc(BUFFER_SAMPLES * sizeof(double)); - ampbuf = malloc(BUFFER_SAMPLES * sizeof(double)); - slopebuf = malloc(BUFFER_SAMPLES * sizeof(double)); - sample_buf = malloc((BASEBAND_SAMPLES_PER_CHIP * BASEBAND_CHIPS_TOTAL) * sizeof(int16_t)); - readpos = 0; - writepos = 1; - - fprintf(stderr, "Samplerate:\t%3.2f MHz\n", (double)samp_rate/1000000); - fprintf(stderr, "Carrier:\t%3.3f MHz\n", (double)carrier_freq/1000000); - fprintf(stderr, "Mod Index:\t%3.1f %%\n", - (double)(mod_index * 100)); - fprintf(stderr, "Chip period:\t%d us\n", chiptime_us); - - pthread_mutex_init(&cb_mutex, NULL); - pthread_mutex_init(&am_mutex, NULL); - pthread_cond_init(&cb_cond, NULL); - pthread_cond_init(&am_cond, NULL); - pthread_attr_init(&attr); - - prepare_baseband(code, sample_buf); - - fl2k_open(&dev, (uint32_t)dev_index); - if (NULL == dev) { - fprintf(stderr, "Failed to open fl2k device #%d.\n", dev_index); - goto out; - } - - r = pthread_create(&am_thread, &attr, am_worker, NULL); - if (r < 0) { - fprintf(stderr, "Error spawning AM worker thread!\n"); - goto out; - } - - pthread_attr_destroy(&attr); - r = fl2k_start_tx(dev, fl2k_callback, NULL, 0); - - /* Set the sample rate */ - r = fl2k_set_sample_rate(dev, samp_rate); - if (r < 0) - fprintf(stderr, "WARNING: Failed to set sample rate. %d\n", r); - - /* read back actual frequency */ - samp_rate = fl2k_get_sample_rate(dev); - - /* Calculate needed constants */ - carrier_per_signal = (int)((double) samp_rate * chiptime_us/(1000000*BASEBAND_SAMPLES_PER_CHIP) + 0.5); - printf("Cps :\t%d\n", carrier_per_signal); -#ifndef _WIN32 - sigact.sa_handler = sighandler; - sigemptyset(&sigact.sa_mask); - sigact.sa_flags = 0; - sigign.sa_handler = SIG_IGN; - sigaction(SIGINT, &sigact, NULL); - sigaction(SIGTERM, &sigact, NULL); - sigaction(SIGQUIT, &sigact, NULL); - sigaction(SIGPIPE, &sigign, NULL); -#else - SetConsoleCtrlHandler( (PHANDLER_ROUTINE) sighandler, TRUE ); -#endif - - am_modulator(code); - -out: - fl2k_close(dev); - - free(ampbuf); - free(slopebuf); - free(buf1); - free(buf2); - - return 0; -} diff --git a/src/fl2k_tcp.c b/src/fl2k_tcp.c deleted file mode 100644 index 871ac26..0000000 --- a/src/fl2k_tcp.c +++ /dev/null @@ -1,256 +0,0 @@ -/* - * osmo-fl2k, turns FL2000-based USB 3.0 to VGA adapters into - * low cost DACs - * - * Copyright (C) 2016-2018 by Steve Markgraf <steve@steve-m.de> - * - * SPDX-License-Identifier: GPL-2.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 2 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 <errno.h> -#include <signal.h> -#include <string.h> -#include <stdio.h> -#include <stdlib.h> - -#ifndef _WIN32 -#include <unistd.h> -#include <arpa/inet.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/time.h> -#include <netinet/in.h> -#include <netinet/tcp.h> /* for TCP_NODELAY */ -#include <fcntl.h> -#define sleep_ms(ms) usleep(ms*1000) -#else -#include <windows.h> -#include <winsock2.h> -#include "getopt/getopt.h" -#define sleep_ms(ms) Sleep(ms) -#endif - -#include "osmo-fl2k.h" - -#ifdef _WIN32 -#pragma comment(lib, "ws2_32.lib") - -typedef int socklen_t; - -#else -#define closesocket close -#define SOCKADDR struct sockaddr -#define SOCKET int -#define SOCKET_ERROR -1 -#endif - -static SOCKET s; -static fl2k_dev_t *dev = NULL; -static volatile int do_exit = 0; -static volatile int connected = 0; -static char *txbuf = NULL; -static fd_set readfds; -static SOCKET sock; - -void usage(void) -{ - fprintf(stderr, - "fl2k_tcp, a spectrum client for FL2K VGA dongles\n\n" - "Usage:\t[-a server address]\n" - "\t[-d device index (default: 0)]\n" - "\t[-p port (default: 1234)]\n" - "\t[-s samplerate in Hz (default: 100 MS/s)]\n" - "\t[-b number of buffers (default: 4)]\n" - ); - exit(1); -} - -#ifdef _WIN32 -BOOL WINAPI -sighandler(int signum) -{ - if (CTRL_C_EVENT == signum) { - fprintf(stderr, "Signal caught, exiting!\n"); - fl2k_stop_tx(dev); - do_exit = 1; - return TRUE; - } - return FALSE; -} -#else -static void sighandler(int signum) -{ - fprintf(stderr, "Signal caught, exiting!\n"); - do_exit = 1; - fl2k_stop_tx(dev); -} -#endif - -void fl2k_callback(fl2k_data_info_t *data_info) -{ - int left = FL2K_BUF_LEN; - int received; - int r; - struct timeval tv = { 1, 0 }; - - if (data_info->device_error) { - fprintf(stderr, "Device error, exiting.\n"); - do_exit = 1; - return; - } - - if (!connected) - return; - - data_info->sampletype_signed = 1; - data_info->r_buf = txbuf; - - while (!do_exit && (left > 0)) { - FD_ZERO(&readfds); - FD_SET(sock, &readfds); - tv.tv_sec = 1; - tv.tv_usec = 0; - r = select(sock + 1, &readfds, NULL, NULL, &tv); - - if (r) { - received = recv(sock, txbuf + (FL2K_BUF_LEN - left), left, 0); - if (!received) { - fprintf(stderr, "Connection was closed!\n"); - fl2k_stop_tx(dev); - do_exit = 1; - } - - left -= received; - } - } -} - -int main(int argc, char **argv) -{ - int r, opt, i; - char *addr = "127.0.0.1"; - int port = 1234; - uint32_t samp_rate = 100000000; - struct sockaddr_in local, remote; - uint32_t buf_num = 0; - int dev_index = 0; - int dev_given = 0; - int flag = 1; - -#ifdef _WIN32 - WSADATA wsd; - i = WSAStartup(MAKEWORD(2,2), &wsd); -#else - struct sigaction sigact, sigign; -#endif - - while ((opt = getopt(argc, argv, "d:s:a:p:b:")) != -1) { - switch (opt) { - case 'd': - dev_index = (uint32_t)atoi(optarg); - dev_given = 1; - break; - case 's': - samp_rate = (uint32_t)atof(optarg); - break; - case 'a': - addr = optarg; - break; - case 'p': - port = atoi(optarg); - break; - case 'b': - buf_num = atoi(optarg); - break; - default: - usage(); - break; - } - } - - if (argc < optind) - usage(); - - if (dev_index < 0) { - exit(1); - } - - txbuf = malloc(FL2K_BUF_LEN); - - if (!txbuf) { - fprintf(stderr, "malloc error!\n"); - exit(1); - } - - fl2k_open(&dev, (uint32_t)dev_index); - if (NULL == dev) { - fprintf(stderr, "Failed to open fl2k device #%d.\n", dev_index); - exit(1); - } - - r = fl2k_start_tx(dev, fl2k_callback, NULL, buf_num); - - /* Set the sample rate */ - r = fl2k_set_sample_rate(dev, samp_rate); - if (r < 0) - fprintf(stderr, "WARNING: Failed to set sample rate.\n"); - -#ifndef _WIN32 - sigact.sa_handler = sighandler; - sigemptyset(&sigact.sa_mask); - sigact.sa_flags = 0; - sigign.sa_handler = SIG_IGN; - sigaction(SIGINT, &sigact, NULL); - sigaction(SIGTERM, &sigact, NULL); - sigaction(SIGQUIT, &sigact, NULL); - sigaction(SIGPIPE, &sigact, NULL); -#else - SetConsoleCtrlHandler( (PHANDLER_ROUTINE) sighandler, TRUE ); -#endif - - sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - - memset(&remote, 0, sizeof(remote)); - - remote.sin_family = AF_INET; - remote.sin_port = htons(port); - remote.sin_addr.s_addr = inet_addr(addr); - - fprintf(stderr, "Connecting to %s:%d...\n", addr, port); - while (connect(sock, (struct sockaddr *)&remote, sizeof(remote)) != 0) { - sleep_ms(500); - if (do_exit) - goto out; - } - - setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *)&flag,sizeof(flag)); - fprintf(stderr, "Connected\n"); - connected = 1; - - while (!do_exit) - sleep_ms(500); - -out: - fl2k_close(dev); - free(txbuf); - closesocket(s); -#ifdef _WIN32 - WSACleanup(); -#endif - - return 0; -} diff --git a/src/fl2k_test.c b/src/fl2k_test.c deleted file mode 100644 index b166dda..0000000 --- a/src/fl2k_test.c +++ /dev/null @@ -1,308 +0,0 @@ -/* - * osmo-fl2k, turns FL2000-based USB 3.0 to VGA adapters into - * low cost DACs - * - * Copyright (C) 2016-2018 by Steve Markgraf <steve@steve-m.de> - * - * based on rtl_test: - * - * Copyright (C) 2012-2014 by Steve Markgraf <steve@steve-m.de> - * Copyright (C) 2012-2014 by Kyle Keen <keenerd@gmail.com> - * Copyright (C) 2014 by Michael Tatarinov <kukabu@gmail.com> - * - * SPDX-License-Identifier: GPL-2.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 2 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 <errno.h> -#include <signal.h> -#include <string.h> -#include <stdio.h> -#include <stdlib.h> -#include <math.h> - -#ifdef __APPLE__ -#include <sys/time.h> -#else -#include <time.h> -#endif - -#ifndef _WIN32 -#include <unistd.h> -#define sleep_ms(ms) usleep(ms*1000) -#else -#include <windows.h> -#include "getopt/getopt.h" -#define sleep_ms(ms) Sleep(ms) -#endif - -#include "osmo-fl2k.h" - -#define DEFAULT_SAMPLE_RATE 100000000 -#define PPM_DURATION 10 -#define PPM_DUMP_TIME 1 - -struct time_generic - /* holds all the platform specific values */ -{ -#ifndef _WIN32 - time_t tv_sec; - long tv_nsec; -#else - long tv_sec; - long tv_nsec; - int init; - LARGE_INTEGER frequency; - LARGE_INTEGER ticks; -#endif -}; - -static int do_exit = 0; -static fl2k_dev_t *dev = NULL; - -static uint32_t samp_rate = DEFAULT_SAMPLE_RATE; -static unsigned int ppm_duration = PPM_DURATION; - -static char *buffer; -static int cb_cnt = 0; - -void usage(void) -{ - fprintf(stderr, - "fl2k_test, clock accuracy test for FL2K VGA dongles,\n" - "also outputs a square wave at fs/2\n\n" - "Usage:\n" - "\t[-d device_index (default: 0)]\n" - "\t[-s samplerate (default: 100 MS/s)]\n" - ); - exit(1); -} - -#ifdef _WIN32 -BOOL WINAPI -sighandler(int signum) -{ - if (CTRL_C_EVENT == signum) { - fprintf(stderr, "Signal caught, exiting!\n"); - fl2k_stop_tx(dev); - do_exit = 1; - return TRUE; - } - return FALSE; -} -#else -static void sighandler(int signum) -{ - fprintf(stderr, "Signal caught, exiting!\n"); - fl2k_stop_tx(dev); - do_exit = 1; -} -#endif - -#ifndef _WIN32 -static int ppm_gettime(struct time_generic *tg) -{ - int rv = ENOSYS; - struct timespec ts; - -#ifdef __unix__ - rv = clock_gettime(CLOCK_MONOTONIC, &ts); - tg->tv_sec = ts.tv_sec; - tg->tv_nsec = ts.tv_nsec; -#elif __APPLE__ - struct timeval tv; - - rv = gettimeofday(&tv, NULL); - tg->tv_sec = tv.tv_sec; - tg->tv_nsec = tv.tv_usec * 1000; -#endif - return rv; -} -#endif - -#ifdef _WIN32 -static int ppm_gettime(struct time_generic *tg) -{ - int rv; - int64_t frac; - if (!tg->init) { - QueryPerformanceFrequency(&tg->frequency); - tg->init = 1; - } - rv = QueryPerformanceCounter(&tg->ticks); - tg->tv_sec = tg->ticks.QuadPart / tg->frequency.QuadPart; - frac = (int64_t)(tg->ticks.QuadPart - (tg->tv_sec * tg->frequency.QuadPart)); - tg->tv_nsec = (long)(frac * 1000000000L / (int64_t)tg->frequency.QuadPart); - return !rv; -} -#endif - -static int ppm_report(uint64_t nsamples, uint64_t interval) -{ - double real_rate, ppm; - - real_rate = nsamples * 1e9 / interval; - ppm = 1e6 * (real_rate / (double)samp_rate - 1.); - return (int)round(ppm); -} - -static void ppm_test(uint32_t len) -{ - static uint64_t nsamples = 0; - static uint64_t interval = 0; - static uint64_t nsamples_total = 0; - static uint64_t interval_total = 0; - static struct time_generic ppm_now; - static struct time_generic ppm_recent; - - static enum { - PPM_INIT_NO, - PPM_INIT_DUMP, - PPM_INIT_RUN - } ppm_init = PPM_INIT_NO; - - ppm_gettime(&ppm_now); - - if (ppm_init != PPM_INIT_RUN) { - /* - * Kyle Keen wrote: - * PPM_DUMP_TIME throws out the first N seconds of data. - * The dongle's PPM is usually very bad when first starting up, - * typically incorrect by more than twice the final value. - * Discarding the first few seconds allows the value to stabilize much faster. - */ - if (ppm_init == PPM_INIT_NO) { - ppm_recent.tv_sec = ppm_now.tv_sec + PPM_DUMP_TIME; - ppm_init = PPM_INIT_DUMP; - return; - } - if (ppm_init == PPM_INIT_DUMP && ppm_recent.tv_sec < ppm_now.tv_sec) - return; - ppm_recent = ppm_now; - ppm_init = PPM_INIT_RUN; - return; - } - nsamples += (uint64_t)len; - interval = (uint64_t)(ppm_now.tv_sec - ppm_recent.tv_sec); - if (interval < ppm_duration) - return; - interval *= 1000000000UL; - interval += (int64_t)(ppm_now.tv_nsec - ppm_recent.tv_nsec); - - nsamples_total += nsamples; - interval_total += interval; - printf("real sample rate: %i current PPM: %i cumulative PPM: %i\n", - (int)((1000000000UL * nsamples) / interval), - ppm_report(nsamples, interval), - ppm_report(nsamples_total, interval_total)); - ppm_recent = ppm_now; - nsamples = 0; -} - -void fl2k_callback(fl2k_data_info_t *data_info) -{ - if (data_info->device_error) { - fprintf(stderr, "Device error, exiting.\n"); - do_exit = 1; - return; - } - - /* drop first couple of callbacks until everything is settled */ - if (cb_cnt > 20) { - ppm_test(FL2K_BUF_LEN); - } else { - /* as data is repetitive, it only needs to be handed - * over until all transfer buffers contain the data */ - data_info->r_buf = buffer; - cb_cnt++; - } -} - -int main(int argc, char **argv) -{ -#ifndef _WIN32 - struct sigaction sigact; -#endif - int r, opt, i; - uint32_t dev_index = 0; - - while ((opt = getopt(argc, argv, "d:s:p::h")) != -1) { - switch (opt) { - case 'd': - dev_index = (uint32_t)atoi(optarg); - break; - case 's': - samp_rate = (uint32_t)atof(optarg); - break; - case 'p': - if (optarg) - ppm_duration = atoi(optarg); - break; - case 'h': - default: - usage(); - break; - } - } - - buffer = malloc(FL2K_BUF_LEN); - if (!buffer) - goto exit; - - fl2k_open(&dev, (uint32_t)dev_index); - if (NULL == dev) { - fprintf(stderr, "Failed to open fl2k device #%d.\n", dev_index); - exit(1); - } - -#ifndef _WIN32 - sigact.sa_handler = sighandler; - sigemptyset(&sigact.sa_mask); - sigact.sa_flags = 0; - sigaction(SIGINT, &sigact, NULL); - sigaction(SIGTERM, &sigact, NULL); - sigaction(SIGQUIT, &sigact, NULL); - sigaction(SIGPIPE, &sigact, NULL); -#else - SetConsoleCtrlHandler( (PHANDLER_ROUTINE) sighandler, TRUE ); -#endif - - /* initialize buffer with rect signal */ - for (i = 0; i < FL2K_BUF_LEN; i += 2) { - buffer[i] = 0x00; - buffer[i+1] = 0xff; - } - - r = fl2k_start_tx(dev, fl2k_callback, NULL, 0); - - /* Set the sample rate */ - r = fl2k_set_sample_rate(dev, samp_rate); - if (r < 0) - fprintf(stderr, "WARNING: Failed to set sample rate.\n"); - - samp_rate = fl2k_get_sample_rate(dev); - - fprintf(stderr, "Reporting PPM error measurement every %u seconds...\n", ppm_duration); - fprintf(stderr, "Press ^C after a few minutes.\n"); - - while (!do_exit) - sleep_ms(500); - -exit: - fl2k_close(dev); - free(buffer); - - return 0; -} diff --git a/src/rds_mod.c b/src/rds_mod.c deleted file mode 100644 index 687aa65..0000000 --- a/src/rds_mod.c +++ /dev/null @@ -1,302 +0,0 @@ -/* - * 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+1]; - char rt[RT_LENGTH+1]; -} 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); - - //'struct tm' has no member named 'tm_gmtoff' on Windows+MinGW - #if defined(__APPLE__) || defined(__FreeBSD__) - offset = utc->tm_gmtoff / (30 * 60); - #else - offset = time(NULL) / (30 * 60); - #endif - - 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; -} diff --git a/src/rds_waveforms.c b/src/rds_waveforms.c deleted file mode 100644 index d508449..0000000 --- a/src/rds_waveforms.c +++ /dev/null @@ -1,153 +0,0 @@ -/* 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, -}; |