diff options
-rw-r--r-- | binding/binding.pri | 6 | ||||
-rw-r--r-- | binding/binding.pro | 11 | ||||
-rw-r--r-- | binding/convenience/convenience.c | 304 | ||||
-rw-r--r-- | binding/convenience/convenience.h | 142 | ||||
-rw-r--r-- | binding/export.map | 1 | ||||
-rw-r--r-- | binding/radio-binding.c | 493 | ||||
-rw-r--r-- | binding/radio_impl.h | 76 | ||||
-rw-r--r-- | binding/radio_impl_rtlsdr.c | 254 | ||||
-rw-r--r-- | binding/radio_output.c | 294 | ||||
-rw-r--r-- | binding/radio_output.h | 31 | ||||
-rw-r--r-- | binding/rtl_fm.c | 1267 | ||||
-rw-r--r-- | binding/rtl_fm.h | 70 | ||||
-rw-r--r-- | package/config.xml | 2 | ||||
-rw-r--r-- | radio.pro | 4 |
14 files changed, 3 insertions, 2952 deletions
diff --git a/binding/binding.pri b/binding/binding.pri deleted file mode 100644 index 3448a56..0000000 --- a/binding/binding.pri +++ /dev/null @@ -1,6 +0,0 @@ -TEMPLATE = lib -CONFIG += plugin use_c_linker -CONFIG -= qt -QMAKE_CFLAGS += -Wextra -Wconversion -Wno-unused-parameter -Werror=maybe-uninitialized -Werror=implicit-function-declaration -ffunction-sections -fdata-sections -Wl,--as-needed -Wl,--gc-sections - -DESTDIR = $${OUT_PWD}/../package/root/lib diff --git a/binding/binding.pro b/binding/binding.pro deleted file mode 100644 index d8c5a93..0000000 --- a/binding/binding.pro +++ /dev/null @@ -1,11 +0,0 @@ -TARGET = radio-binding - -HEADERS = radio_impl.h radio_output.h rtl_fm.h convenience/convenience.h -SOURCES = radio-binding.c radio_output.c radio_impl_rtlsdr.c rtl_fm.c convenience/convenience.c - -LIBS += -Wl,--version-script=$$PWD/export.map - -CONFIG += link_pkgconfig -PKGCONFIG += json-c afb-daemon librtlsdr glib-2.0 libpulse-simple - -include(binding.pri) diff --git a/binding/convenience/convenience.c b/binding/convenience/convenience.c deleted file mode 100644 index 517dc4e..0000000 --- a/binding/convenience/convenience.c +++ /dev/null @@ -1,304 +0,0 @@ -/* - * Copyright (C) 2014 by Kyle Keen <keenerd@gmail.com> - * - * 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/>. - */ - -/* a collection of user friendly tools - * todo: use strtol for more flexible int parsing - * */ - -#include <string.h> -#include <stdio.h> -#include <stdlib.h> - -#ifndef _WIN32 -#include <unistd.h> -#else -#include <windows.h> -#include <fcntl.h> -#include <io.h> -#define _USE_MATH_DEFINES -#endif - -#include <math.h> - -#include "rtl-sdr.h" - -double atofs(char *s) -/* standard suffixes */ -{ - char last; - int len; - double suff = 1.0; - len = strlen(s); - last = s[len-1]; - s[len-1] = '\0'; - switch (last) { - case 'g': - case 'G': - suff *= 1e3; - case 'm': - case 'M': - suff *= 1e3; - case 'k': - case 'K': - suff *= 1e3; - suff *= atof(s); - s[len-1] = last; - return suff; - } - s[len-1] = last; - return atof(s); -} - -double atoft(char *s) -/* time suffixes, returns seconds */ -{ - char last; - int len; - double suff = 1.0; - len = strlen(s); - last = s[len-1]; - s[len-1] = '\0'; - switch (last) { - case 'h': - case 'H': - suff *= 60; - case 'm': - case 'M': - suff *= 60; - case 's': - case 'S': - suff *= atof(s); - s[len-1] = last; - return suff; - } - s[len-1] = last; - return atof(s); -} - -double atofp(char *s) -/* percent suffixes */ -{ - char last; - int len; - double suff = 1.0; - len = strlen(s); - last = s[len-1]; - s[len-1] = '\0'; - switch (last) { - case '%': - suff *= 0.01; - suff *= atof(s); - s[len-1] = last; - return suff; - } - s[len-1] = last; - return atof(s); -} - -int nearest_gain(rtlsdr_dev_t *dev, int target_gain) -{ - int i, r, err1, err2, count, nearest; - int* gains; - r = rtlsdr_set_tuner_gain_mode(dev, 1); - if (r < 0) { - fprintf(stderr, "WARNING: Failed to enable manual gain.\n"); - return r; - } - count = rtlsdr_get_tuner_gains(dev, NULL); - if (count <= 0) { - return 0; - } - gains = malloc(sizeof(int) * count); - count = rtlsdr_get_tuner_gains(dev, gains); - nearest = gains[0]; - for (i=0; i<count; i++) { - err1 = abs(target_gain - nearest); - err2 = abs(target_gain - gains[i]); - if (err2 < err1) { - nearest = gains[i]; - } - } - free(gains); - return nearest; -} - -int verbose_set_frequency(rtlsdr_dev_t *dev, uint32_t frequency) -{ - int r; - r = rtlsdr_set_center_freq(dev, frequency); - if (r < 0) { - fprintf(stderr, "WARNING: Failed to set center freq.\n"); - } else { - fprintf(stderr, "Tuned to %u Hz.\n", frequency); - } - return r; -} - -int verbose_set_sample_rate(rtlsdr_dev_t *dev, uint32_t samp_rate) -{ - int r; - r = rtlsdr_set_sample_rate(dev, samp_rate); - if (r < 0) { - fprintf(stderr, "WARNING: Failed to set sample rate.\n"); - } else { - fprintf(stderr, "Sampling at %u S/s.\n", samp_rate); - } - return r; -} - -int verbose_direct_sampling(rtlsdr_dev_t *dev, int on) -{ - int r; - r = rtlsdr_set_direct_sampling(dev, on); - if (r != 0) { - fprintf(stderr, "WARNING: Failed to set direct sampling mode.\n"); - return r; - } - if (on == 0) { - fprintf(stderr, "Direct sampling mode disabled.\n");} - if (on == 1) { - fprintf(stderr, "Enabled direct sampling mode, input 1/I.\n");} - if (on == 2) { - fprintf(stderr, "Enabled direct sampling mode, input 2/Q.\n");} - return r; -} - -int verbose_offset_tuning(rtlsdr_dev_t *dev) -{ - int r; - r = rtlsdr_set_offset_tuning(dev, 1); - if (r != 0) { - fprintf(stderr, "WARNING: Failed to set offset tuning.\n"); - } else { - fprintf(stderr, "Offset tuning mode enabled.\n"); - } - return r; -} - -int verbose_auto_gain(rtlsdr_dev_t *dev) -{ - int r; - r = rtlsdr_set_tuner_gain_mode(dev, 0); - if (r != 0) { - fprintf(stderr, "WARNING: Failed to set tuner gain.\n"); - } else { - fprintf(stderr, "Tuner gain set to automatic.\n"); - } - return r; -} - -int verbose_gain_set(rtlsdr_dev_t *dev, int gain) -{ - int r; - r = rtlsdr_set_tuner_gain_mode(dev, 1); - if (r < 0) { - fprintf(stderr, "WARNING: Failed to enable manual gain.\n"); - return r; - } - r = rtlsdr_set_tuner_gain(dev, gain); - if (r != 0) { - fprintf(stderr, "WARNING: Failed to set tuner gain.\n"); - } else { - fprintf(stderr, "Tuner gain set to %0.2f dB.\n", gain/10.0); - } - return r; -} - -int verbose_ppm_set(rtlsdr_dev_t *dev, int ppm_error) -{ - int r; - if (ppm_error == 0) { - return 0;} - r = rtlsdr_set_freq_correction(dev, ppm_error); - if (r < 0) { - fprintf(stderr, "WARNING: Failed to set ppm error.\n"); - } else { - fprintf(stderr, "Tuner error set to %i ppm.\n", ppm_error); - } - return r; -} - -int verbose_reset_buffer(rtlsdr_dev_t *dev) -{ - int r; - r = rtlsdr_reset_buffer(dev); - if (r < 0) { - fprintf(stderr, "WARNING: Failed to reset buffers.\n");} - return r; -} - -int verbose_device_search(char *s) -{ - int i, device_count, device, offset; - char *s2; - char vendor[256], product[256], serial[256]; - device_count = rtlsdr_get_device_count(); - if (!device_count) { - fprintf(stderr, "No supported devices found.\n"); - return -1; - } - fprintf(stderr, "Found %d device(s):\n", device_count); - for (i = 0; i < device_count; i++) { - rtlsdr_get_device_usb_strings(i, vendor, product, serial); - fprintf(stderr, " %d: %s, %s, SN: %s\n", i, vendor, product, serial); - } - fprintf(stderr, "\n"); - /* does string look like raw id number */ - device = (int)strtol(s, &s2, 0); - if (s2[0] == '\0' && device >= 0 && device < device_count) { - fprintf(stderr, "Using device %d: %s\n", - device, rtlsdr_get_device_name((uint32_t)device)); - return device; - } - /* does string exact match a serial */ - for (i = 0; i < device_count; i++) { - rtlsdr_get_device_usb_strings(i, vendor, product, serial); - if (strcmp(s, serial) != 0) { - continue;} - device = i; - fprintf(stderr, "Using device %d: %s\n", - device, rtlsdr_get_device_name((uint32_t)device)); - return device; - } - /* does string prefix match a serial */ - for (i = 0; i < device_count; i++) { - rtlsdr_get_device_usb_strings(i, vendor, product, serial); - if (strncmp(s, serial, strlen(s)) != 0) { - continue;} - device = i; - fprintf(stderr, "Using device %d: %s\n", - device, rtlsdr_get_device_name((uint32_t)device)); - return device; - } - /* does string suffix match a serial */ - for (i = 0; i < device_count; i++) { - rtlsdr_get_device_usb_strings(i, vendor, product, serial); - offset = strlen(serial) - strlen(s); - if (offset < 0) { - continue;} - if (strncmp(s, serial+offset, strlen(s)) != 0) { - continue;} - device = i; - fprintf(stderr, "Using device %d: %s\n", - device, rtlsdr_get_device_name((uint32_t)device)); - return device; - } - fprintf(stderr, "No matching devices found.\n"); - return -1; -} - -// vim: tabstop=8:softtabstop=8:shiftwidth=8:noexpandtab diff --git a/binding/convenience/convenience.h b/binding/convenience/convenience.h deleted file mode 100644 index 1faa2af..0000000 --- a/binding/convenience/convenience.h +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright (C) 2014 by Kyle Keen <keenerd@gmail.com> - * - * 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/>. - */ - -/* a collection of user friendly tools */ - -/*! - * Convert standard suffixes (k, M, G) to double - * - * \param s a string to be parsed - * \return double - */ - -double atofs(char *s); - -/*! - * Convert time suffixes (s, m, h) to double - * - * \param s a string to be parsed - * \return seconds as double - */ - -double atoft(char *s); - -/*! - * Convert percent suffixe (%) to double - * - * \param s a string to be parsed - * \return double - */ - -double atofp(char *s); - -/*! - * Find nearest supported gain - * - * \param dev the device handle given by rtlsdr_open() - * \param target_gain in tenths of a dB - * \return 0 on success - */ - -int nearest_gain(rtlsdr_dev_t *dev, int target_gain); - -/*! - * Set device frequency and report status on stderr - * - * \param dev the device handle given by rtlsdr_open() - * \param frequency in Hz - * \return 0 on success - */ - -int verbose_set_frequency(rtlsdr_dev_t *dev, uint32_t frequency); - -/*! - * Set device sample rate and report status on stderr - * - * \param dev the device handle given by rtlsdr_open() - * \param samp_rate in samples/second - * \return 0 on success - */ - -int verbose_set_sample_rate(rtlsdr_dev_t *dev, uint32_t samp_rate); - -/*! - * Enable or disable the direct sampling mode and report status on stderr - * - * \param dev the device handle given by rtlsdr_open() - * \param on 0 means disabled, 1 I-ADC input enabled, 2 Q-ADC input enabled - * \return 0 on success - */ - -int verbose_direct_sampling(rtlsdr_dev_t *dev, int on); - -/*! - * Enable offset tuning and report status on stderr - * - * \param dev the device handle given by rtlsdr_open() - * \return 0 on success - */ - -int verbose_offset_tuning(rtlsdr_dev_t *dev); - -/*! - * Enable auto gain and report status on stderr - * - * \param dev the device handle given by rtlsdr_open() - * \return 0 on success - */ - -int verbose_auto_gain(rtlsdr_dev_t *dev); - -/*! - * Set tuner gain and report status on stderr - * - * \param dev the device handle given by rtlsdr_open() - * \param gain in tenths of a dB - * \return 0 on success - */ - -int verbose_gain_set(rtlsdr_dev_t *dev, int gain); - -/*! - * Set the frequency correction value for the device and report status on stderr. - * - * \param dev the device handle given by rtlsdr_open() - * \param ppm_error correction value in parts per million (ppm) - * \return 0 on success - */ - -int verbose_ppm_set(rtlsdr_dev_t *dev, int ppm_error); - -/*! - * Reset buffer - * - * \param dev the device handle given by rtlsdr_open() - * \return 0 on success - */ - -int verbose_reset_buffer(rtlsdr_dev_t *dev); - -/*! - * Find the closest matching device. - * - * \param s a string to be parsed - * \return dev_index int, -1 on error - */ - -int verbose_device_search(char *s); - diff --git a/binding/export.map b/binding/export.map deleted file mode 100644 index 52c1b4a..0000000 --- a/binding/export.map +++ /dev/null @@ -1 +0,0 @@ -{ global: afbBindingV1*; local: *; }; diff --git a/binding/radio-binding.c b/binding/radio-binding.c deleted file mode 100644 index 12ed966..0000000 --- a/binding/radio-binding.c +++ /dev/null @@ -1,493 +0,0 @@ -/* - * Copyright (C) 2017 Konsulko Group - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define _GNU_SOURCE - -#include <stdlib.h> -#include <string.h> -#include <stdint.h> -#include <unistd.h> -#include <sys/types.h> - -#include <json-c/json.h> -#include <afb/afb-binding.h> -#include <afb/afb-service-itf.h> - -#include "radio_impl.h" - -static const struct afb_binding_interface *interface; - -static struct afb_event freq_event; -static struct afb_event scan_event; - -static void freq_callback(uint32_t frequency, void *data) -{ - json_object *jresp = json_object_new_object(); - json_object *value = json_object_new_int((int) frequency); - - json_object_object_add(jresp, "value", value); - afb_event_push(freq_event, json_object_get(jresp)); -} - -static void scan_callback(uint32_t frequency, void *data) -{ - json_object *jresp = json_object_new_object(); - json_object *value = json_object_new_int((int) frequency); - - json_object_object_add(jresp, "value", value); - afb_event_push(scan_event, json_object_get(jresp)); -} - -/* - * Binding verb handlers - */ - -/* - * @brief Get (and optionally set) frequency - * - * @param struct afb_req : an afb request structure - * - */ -static void frequency(struct afb_req request) -{ - json_object *ret_json; - const char *value = afb_req_value(request, "value"); - uint32_t frequency; - - if(value) { - char *p; - frequency = strtoul(value, &p, 10); - if(frequency && *p == '\0') { - radio_impl_set_frequency(frequency); - } else { - afb_req_fail(request, "failed", "Invalid scan direction"); - return; - } - } - ret_json = json_object_new_object(); - frequency = radio_impl_get_frequency(); - json_object_object_add(ret_json, "frequency", json_object_new_int((int32_t) frequency)); - afb_req_success(request, ret_json, NULL); -} - -/* - * @brief Get (and optionally set) frequency band - * - * @param struct afb_req : an afb request structure - * - */ -static void band(struct afb_req request) -{ - json_object *ret_json; - const char *value = afb_req_value(request, "value"); - int valid = 0; - radio_band_t band; - char band_name[4]; - - if(value) { - if(!strcasecmp(value, "AM")) { - band = BAND_AM; - valid = 1; - } else if(!strcasecmp(value, "FM")) { - band = BAND_FM; - valid = 1; - } else { - char *p; - band = strtoul(value, &p, 10); - if(p != value && *p == '\0') { - switch(band) { - case BAND_AM: - case BAND_FM: - valid = 1; - break; - default: - break; - } - } - } - if(valid) { - radio_impl_set_band(band); - } else { - afb_req_fail(request, "failed", "Invalid band"); - return; - } - } - ret_json = json_object_new_object(); - band = radio_impl_get_band(); - sprintf(band_name, "%s", band == BAND_AM ? "AM" : "FM"); - json_object_object_add(ret_json, "band", json_object_new_string(band_name)); - afb_req_success(request, ret_json, NULL); -} - -/* - * @brief Check if band is supported - * - * @param struct afb_req : an afb request structure - * - */ -static void band_supported(struct afb_req request) -{ - json_object *ret_json; - const char *value = afb_req_value(request, "band"); - int valid = 0; - radio_band_t band; - - if(value) { - if(!strcasecmp(value, "AM")) { - band = BAND_AM; - valid = 1; - } else if(!strcasecmp(value, "FM")) { - band = BAND_FM; - valid = 1; - } else { - char *p; - band = strtoul(value, &p, 10); - if(p != value && *p == '\0') { - switch(band) { - case BAND_AM: - case BAND_FM: - valid = 1; - break; - default: - break; - } - } - } - } - if(!valid) { - afb_req_fail(request, "failed", "Invalid band"); - return; - } - ret_json = json_object_new_object(); - json_object_object_add(ret_json, - "supported", - json_object_new_int(radio_impl_band_supported(band))); - afb_req_success(request, ret_json, NULL); -} - -/* - * @brief Get frequency range for a band - * - * @param struct afb_req : an afb request structure - * - */ -static void frequency_range(struct afb_req request) -{ - json_object *ret_json; - const char *value = afb_req_value(request, "band"); - int valid = 0; - radio_band_t band; - uint32_t min_frequency; - uint32_t max_frequency; - - if(value) { - if(!strcasecmp(value, "AM")) { - band = BAND_AM; - valid = 1; - } else if(!strcasecmp(value, "FM")) { - band = BAND_FM; - valid = 1; - } else { - char *p; - band = strtoul(value, &p, 10); - if(p != value && *p == '\0') { - switch(band) { - case BAND_AM: - case BAND_FM: - valid = 1; - break; - default: - break; - } - } - } - } - if(!valid) { - afb_req_fail(request, "failed", "Invalid band"); - return; - } - ret_json = json_object_new_object(); - min_frequency = radio_impl_get_min_frequency(band); - max_frequency = radio_impl_get_max_frequency(band); - json_object_object_add(ret_json, "min", json_object_new_int((int32_t) min_frequency)); - json_object_object_add(ret_json, "max", json_object_new_int((int32_t) max_frequency)); - afb_req_success(request, ret_json, NULL); -} - -/* - * @brief Get frequency step size (Hz) for a band - * - * @param struct afb_req : an afb request structure - * - */ -static void frequency_step(struct afb_req request) -{ - json_object *ret_json; - const char *value = afb_req_value(request, "band"); - int valid = 0; - radio_band_t band; - uint32_t step; - - if(value) { - if(!strcasecmp(value, "AM")) { - band = BAND_AM; - valid = 1; - } else if(!strcasecmp(value, "FM")) { - band = BAND_FM; - valid = 1; - } else { - char *p; - band = strtoul(value, &p, 10); - if(p != value && *p == '\0') { - switch(band) { - case BAND_AM: - case BAND_FM: - valid = 1; - break; - default: - break; - } - } - } - } - if(!valid) { - afb_req_fail(request, "failed", "Invalid band"); - return; - } - ret_json = json_object_new_object(); - step = radio_impl_get_frequency_step(band); - json_object_object_add(ret_json, "step", json_object_new_int((int32_t) step)); - afb_req_success(request, ret_json, NULL); -} - -/* - * @brief Start radio playback - * - * @param struct afb_req : an afb request structure - * - */ -static void start(struct afb_req request) -{ - radio_impl_start(); - afb_req_success(request, NULL, NULL); -} - -/* - * @brief Stop radio playback - * - * @param struct afb_req : an afb request structure - * - */ -static void stop(struct afb_req request) -{ - radio_impl_stop(); - afb_req_success(request, NULL, NULL); -} - -/* - * @brief Scan for a station in the specified direction - * - * @param struct afb_req : an afb request structure - * - */ -static void scan_start(struct afb_req request) -{ - json_object *ret_json; - const char *value = afb_req_value(request, "direction"); - int valid = 0; - radio_scan_direction_t direction; - - if(value) { - if(!strcasecmp(value, "forward")) { - direction = SCAN_FORWARD; - valid = 1; - } else if(!strcasecmp(value, "backward")) { - direction = SCAN_BACKWARD; - valid = 1; - } else { - char *p; - direction = strtoul(value, &p, 10); - if(p != value && *p == '\0') { - switch(direction) { - case SCAN_FORWARD: - case SCAN_BACKWARD: - valid = 1; - break; - default: - break; - } - } - } - } - if(!valid) { - afb_req_fail(request, "failed", "Invalid direction"); - return; - } - radio_impl_scan_start(direction, scan_callback, NULL); - afb_req_success(request, ret_json, NULL); -} - -/* - * @brief Stop station scan - * - * @param struct afb_req : an afb request structure - * - */ -static void scan_stop(struct afb_req request) -{ - radio_impl_scan_stop(); - afb_req_success(request, NULL, NULL); -} - -/* - * @brief Get (and optionally set) stereo mode - * - * @param struct afb_req : an afb request structure - * - */ -static void stereo_mode(struct afb_req request) -{ - json_object *ret_json; - const char *value = afb_req_value(request, "value"); - int valid = 0; - radio_stereo_mode_t mode; - char mode_name[4]; - - if(value) { - if(!strcasecmp(value, "mono")) { - mode = MONO; - valid = 1; - } else if(!strcasecmp(value, "stereo")) { - mode = STEREO; - valid = 1; - } else { - char *p; - mode = strtoul(value, &p, 10); - if(p != value && *p == '\0') { - switch(mode) { - case MONO: - case STEREO: - valid = 1; - break; - default: - break; - } - } - } - if(valid) { - radio_impl_set_stereo_mode(mode); - } else { - afb_req_fail(request, "failed", "Invalid mode"); - return; - } - } - ret_json = json_object_new_object(); - mode = radio_impl_get_stereo_mode(); - sprintf(mode_name, "%s", mode == MONO ? "mono" : "stereo"); - json_object_object_add(ret_json, "mode", json_object_new_string(mode_name)); - afb_req_success(request, ret_json, NULL); -} - -/* - * @brief Subscribe for an event - * - * @param struct afb_req : an afb request structure - * - */ -static void subscribe(struct afb_req request) -{ - const char *value = afb_req_value(request, "value"); - if(value) { - if(!strcasecmp(value, "frequency")) { - afb_req_subscribe(request, freq_event); - } else if(!strcasecmp(value, "station_found")) { - afb_req_subscribe(request, scan_event); - } else { - afb_req_fail(request, "failed", "Invalid event"); - return; - } - } - afb_req_success(request, NULL, NULL); -} - -/* - * @brief Unsubscribe for an event - * - * @param struct afb_req : an afb request structure - * - */ -static void unsubscribe(struct afb_req request) -{ - const char *value = afb_req_value(request, "value"); - if(value) { - if(!strcasecmp(value, "frequency")) { - afb_req_unsubscribe(request, freq_event); - } else if(!strcasecmp(value, "station_found")) { - afb_req_unsubscribe(request, scan_event); - } else { - afb_req_fail(request, "failed", "Invalid event"); - return; - } - } - afb_req_success(request, NULL, NULL); -} - -static const struct afb_verb_desc_v1 verbs[]= { - { "frequency", AFB_SESSION_CHECK, frequency, "Get/Set frequency" }, - { "band", AFB_SESSION_CHECK, band, "Get/Set band" }, - { "band_supported", AFB_SESSION_CHECK, band_supported, "Check band support" }, - { "frequency_range", AFB_SESSION_CHECK, frequency_range, "Get frequency range" }, - { "frequency_step", AFB_SESSION_CHECK, frequency_step, "Get frequency step" }, - { "start", AFB_SESSION_CHECK, start, "Start radio playback" }, - { "stop", AFB_SESSION_CHECK, stop, "Stop radio playback" }, - { "scan_start", AFB_SESSION_CHECK, scan_start, "Start station scan" }, - { "scan_stop", AFB_SESSION_CHECK, scan_stop, "Stop station scan" }, - { "stereo_mode", AFB_SESSION_CHECK, stereo_mode, "Get/Set stereo_mode" }, - { "subscribe", AFB_SESSION_CHECK, subscribe, "Subscribe for an event" }, - { "unsubscribe", AFB_SESSION_CHECK, unsubscribe, "Unsubscribe for an event" }, - { NULL } -}; - -static const struct afb_binding binding_desc = { - .type = AFB_BINDING_VERSION_1, - .v1 = { - .info = "radio service", - .prefix = "radio", - .verbs = verbs - } -}; - -const struct afb_binding *afbBindingV1Register (const struct afb_binding_interface *itf) -{ - interface = itf; - - return &binding_desc; -} - -int afbBindingV1ServiceInit(struct afb_service service) -{ - int rc; - - freq_event = afb_daemon_make_event(interface->daemon, "frequency"); - scan_event = afb_daemon_make_event(interface->daemon, "station_found"); - - rc = radio_impl_init(); - if(rc == 0) { - radio_impl_set_frequency_callback(freq_callback, NULL); - } - - return rc; -} diff --git a/binding/radio_impl.h b/binding/radio_impl.h deleted file mode 100644 index 79e91a4..0000000 --- a/binding/radio_impl.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) 2017 Konsulko Group - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _RADIO_IMPL_H -#define _RADIO_IMPL_H - -#include <stdint.h> - -typedef enum { - BAND_AM = 0, - BAND_FM -} radio_band_t; - -typedef enum { - SCAN_FORWARD = 0, - SCAN_BACKWARD -} radio_scan_direction_t; - -typedef void (*radio_scan_callback_t)(uint32_t frequency, void *data); - -typedef void (*radio_freq_callback_t)(uint32_t frequency, void *data); - -typedef enum { - MONO = 0, - STEREO -} radio_stereo_mode_t; - -int radio_impl_init(void); - -uint32_t radio_impl_get_frequency(void); - -void radio_impl_set_frequency(uint32_t frequency); - -void radio_impl_set_frequency_callback(radio_freq_callback_t callback, - void *data); - -radio_band_t radio_impl_get_band(void); - -void radio_impl_set_band(radio_band_t band); - -int radio_impl_band_supported(radio_band_t band); - -uint32_t radio_impl_get_min_frequency(radio_band_t band); - -uint32_t radio_impl_get_max_frequency(radio_band_t band); - -uint32_t radio_impl_get_frequency_step(radio_band_t band); - -void radio_impl_start(void); - -void radio_impl_stop(void); - -void radio_impl_scan_start(radio_scan_direction_t direction, - radio_scan_callback_t callback, - void *data); - -void radio_impl_scan_stop(void); - -radio_stereo_mode_t radio_impl_get_stereo_mode(void); - -void radio_impl_set_stereo_mode(radio_stereo_mode_t mode); - -#endif /* _RADIO_IMPL_H */ diff --git a/binding/radio_impl_rtlsdr.c b/binding/radio_impl_rtlsdr.c deleted file mode 100644 index 4364fd5..0000000 --- a/binding/radio_impl_rtlsdr.c +++ /dev/null @@ -1,254 +0,0 @@ -/* - * Copyright (C) 2017 Konsulko Group - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <stdio.h> -#include <stdlib.h> -#include <stdint.h> -#include <stdbool.h> -#include <string.h> -#include <glib.h> - -#include "radio_impl.h" -#include "radio_output.h" -#include "rtl_fm.h" - -// Structure to describe FM band plans, all values in Hz. -typedef struct { - char *name; - uint32_t min; - uint32_t max; - uint32_t step; -} fm_band_plan_t; - -static fm_band_plan_t known_fm_band_plans[5] = { - { .name = "US", .min = 87900000, .max = 107900000, .step = 200000 }, - { .name = "JP", .min = 76100000, .max = 89900000, .step = 100000 }, - { .name = "EU", .min = 87500000, .max = 108000000, .step = 50000 }, - { .name = "ITU-1", .min = 87500000, .max = 108000000, .step = 50000 }, - { .name = "ITU-2", .min = 87900000, .max = 107900000, .step = 50000 } -}; - -static unsigned int bandplan; -static bool present; -static bool active; -static uint32_t current_frequency; - -static void rtl_output_callback(int16_t *result, int result_len, void *ctx) -{ - if(active) - radio_output_write((char*) result, result_len * 2); -} - -int radio_impl_init(void) -{ - GKeyFile* conf_file; - int conf_file_present = 0; - char *value_str; - - if(present) - return -1; - - // Load settings from configuration file if it exists - conf_file = g_key_file_new(); - if(conf_file && - g_key_file_load_from_dirs(conf_file, - "AGL.conf", - (const gchar**) g_get_system_config_dirs(), - NULL, - G_KEY_FILE_KEEP_COMMENTS, - NULL) == TRUE) { - conf_file_present = 1; - - // Set band plan if it is specified - value_str = g_key_file_get_string(conf_file, - "radio", - "fmbandplan", - NULL); - if(value_str) { - unsigned int i; - for(i = 0; - i < sizeof(known_fm_band_plans) / sizeof(fm_band_plan_t); - i++) { - if(!strcasecmp(value_str, known_fm_band_plans[i].name)) { - bandplan = i; - break; - } - } - } - } - fprintf(stderr, "Using FM Bandplan: %s\n", known_fm_band_plans[bandplan].name); - - current_frequency = radio_impl_get_min_frequency(BAND_FM); - if(rtl_fm_init(current_frequency, 200000, 48000, rtl_output_callback, NULL) < 0) { - return -1; - } - - if(conf_file_present) { - GError *error = NULL; - int n; - - // Allow over-riding scanning parameters just in case a demo - // setup needs to do so to work reliably. - n = g_key_file_get_integer(conf_file, - "radio", - "scan_squelch_level", - &error); - //error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND && - //error->code != G_KEY_FILE_ERROR_INVALID_VALUE) { - if(!error) { - fprintf(stderr, "Scanning squelch level set to %d\n", n); - rtl_fm_scan_set_squelch_level(n); - } - - error = NULL; - n = g_key_file_get_integer(conf_file, - "radio", - "scan_squelch_limit", - &error); - if(!error) { - fprintf(stderr, "Scanning squelch limit set to %d\n", n); - rtl_fm_scan_set_squelch_limit(n); - } - - g_key_file_free(conf_file); - } - - present = true; - return 0; -} - -uint32_t radio_impl_get_frequency(void) -{ - return current_frequency; -} - -void radio_impl_set_frequency(uint32_t frequency) -{ - if(!present) - return; - - if(frequency < known_fm_band_plans[bandplan].min || - frequency > known_fm_band_plans[bandplan].max) - return; - - radio_impl_scan_stop(); - current_frequency = frequency; - rtl_fm_set_freq(frequency); -} - -void radio_impl_set_frequency_callback(radio_freq_callback_t callback, - void *data) -{ - rtl_fm_set_freq_callback(callback, data); -} - -radio_band_t radio_impl_get_band(void) -{ - return BAND_FM; -} - -void radio_impl_set_band(radio_band_t band) -{ - // We only support FM, so do nothing -} - -int radio_impl_band_supported(radio_band_t band) -{ - if(band == BAND_FM) - return 1; - return 0; -} - -uint32_t radio_impl_get_min_frequency(radio_band_t band) -{ - return known_fm_band_plans[bandplan].min; -} - -uint32_t radio_impl_get_max_frequency(radio_band_t band) -{ - return known_fm_band_plans[bandplan].max; -} - -uint32_t radio_impl_get_frequency_step(radio_band_t band) -{ - uint32_t ret = 0; - - switch (band) { - case BAND_AM: - ret = 1000; // 1 kHz - break; - case BAND_FM: - ret = known_fm_band_plans[bandplan].step; - break; - default: - break; - } - return ret; -} - -void radio_impl_start(void) -{ - if(!present) - return; - - if(!active) { - if(radio_output_start() != 0) - return; - - rtl_fm_start(); - active = true; - } -} - -void radio_impl_stop(void) -{ - if(!present) - return; - - if(active) { - active = false; - radio_output_stop(); - rtl_fm_stop(); - - } -} - -void radio_impl_scan_start(radio_scan_direction_t direction, - radio_scan_callback_t callback, - void *data) -{ - rtl_fm_scan_start(direction == SCAN_FORWARD ? 0 : 1, - callback, - data, - radio_impl_get_frequency_step(BAND_FM), - radio_impl_get_min_frequency(BAND_FM), - radio_impl_get_max_frequency(BAND_FM)); -} - -void radio_impl_scan_stop(void) -{ - rtl_fm_scan_stop(); -} - -radio_stereo_mode_t radio_impl_get_stereo_mode(void) -{ - return STEREO; -} - -void radio_impl_set_stereo_mode(radio_stereo_mode_t mode) -{ - // We only support stereo, so do nothing -} diff --git a/binding/radio_output.c b/binding/radio_output.c deleted file mode 100644 index a49687b..0000000 --- a/binding/radio_output.c +++ /dev/null @@ -1,294 +0,0 @@ -/* - * Copyright (C) 2017 Konsulko Group - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <pulse/pulseaudio.h> - -#include "radio_output.h" -#include "rtl_fm.h" - -static pa_threaded_mainloop *mainloop; -static pa_context *context; -static pa_stream *stream; - -static unsigned int extra; -static int16_t extra_buf[1]; -static unsigned char *output_buf; - -static void pa_context_state_cb(pa_context *c, void *data) { - pa_operation *o; - - assert(c); - switch (pa_context_get_state(c)) { - case PA_CONTEXT_CONNECTING: - case PA_CONTEXT_AUTHORIZING: - case PA_CONTEXT_SETTING_NAME: - case PA_CONTEXT_READY: - break; - case PA_CONTEXT_TERMINATED: - pa_threaded_mainloop_stop(mainloop); - break; - case PA_CONTEXT_FAILED: - default: - fprintf(stderr, "PA connection failed: %s\n", - pa_strerror(pa_context_errno(c))); - pa_threaded_mainloop_stop(mainloop); - break; - } - pa_threaded_mainloop_signal(mainloop, 0); -} - -int radio_output_open(void) -{ - pa_context *c; - pa_mainloop_api *mapi; - char *client; - - if(context) - return 0; - - if (!(mainloop = pa_threaded_mainloop_new())) { - fprintf(stderr, "pa_mainloop_new() failed.\n"); - return -1; - } - - pa_threaded_mainloop_set_name(mainloop, "pa_mainloop"); - mapi = pa_threaded_mainloop_get_api(mainloop); - - client = pa_xstrdup("radio"); - if (!(c = pa_context_new(mapi, client))) { - fprintf(stderr, "pa_context_new() failed.\n"); - goto exit; - } - - pa_context_set_state_callback(c, pa_context_state_cb, NULL); - if (pa_context_connect(c, NULL, 0, NULL) < 0) { - fprintf(stderr, "pa_context_connect(): %s", pa_strerror(pa_context_errno(c))); - goto exit; - } - - if (pa_threaded_mainloop_start(mainloop) < 0) { - fprintf(stderr, "pa_mainloop_run() failed.\n"); - goto exit; - } - - context = c; - - extra = 0; - output_buf = malloc(sizeof(unsigned char) * RTL_FM_MAXIMUM_BUF_LENGTH); - - return 0; - -exit: - if (c) - pa_context_unref(c); - - if (mainloop) - pa_threaded_mainloop_free(mainloop); - - pa_xfree(client); - return -1; -} - -int radio_output_start(void) -{ - int error = 0; - pa_sample_spec *spec; - - if(stream) - return 0; - - if(!context) { - error = radio_output_open(); - if(error != 0) - return error; - } - - while(pa_context_get_state(context) != PA_CONTEXT_READY) - pa_threaded_mainloop_wait(mainloop); - - spec = (pa_sample_spec*) calloc(1, sizeof(pa_sample_spec)); - spec->format = PA_SAMPLE_S16LE; - spec->rate = 24000; - spec->channels = 2; - if (!pa_sample_spec_valid(spec)) { - fprintf(stderr, "%s\n", - pa_strerror(pa_context_errno(context))); - return -1; - } - - pa_threaded_mainloop_lock(mainloop); - pa_proplist *props = pa_proplist_new(); - pa_proplist_sets(props, PA_PROP_MEDIA_ROLE, "radio"); - stream = pa_stream_new_with_proplist(context, "radio-output", spec, 0, props); - if(!stream) { - fprintf(stderr, "Error creating stream %s\n", - pa_strerror(pa_context_errno(context))); - pa_proplist_free(props); - free(spec); - pa_threaded_mainloop_unlock(mainloop); - return -1; - } - pa_proplist_free(props); - free(spec); - - if(pa_stream_connect_playback(stream, - NULL, - NULL, - (pa_stream_flags_t) 0, - NULL, - NULL) < 0) { - fprintf(stderr, "Error connecting to PulseAudio : %s\n", - pa_strerror(pa_context_errno(context))); - pa_stream_unref(stream); - stream = NULL; - pa_threaded_mainloop_unlock(mainloop); - return -1; - } - - pa_threaded_mainloop_unlock(mainloop); - - while(pa_stream_get_state(stream) != PA_STREAM_READY) - pa_threaded_mainloop_wait(mainloop); - - return error; -} - -void radio_output_stop(void) -{ - if(stream) { - pa_threaded_mainloop_lock(mainloop); - - pa_stream_set_state_callback(stream, 0, 0); - pa_stream_set_write_callback(stream, 0, 0); - pa_stream_set_underflow_callback(stream, 0, 0); - pa_stream_set_overflow_callback(stream, 0, 0); - pa_stream_set_latency_update_callback(stream, 0, 0); - - pa_operation *o = pa_stream_flush(stream, NULL, NULL); - if(o) - pa_operation_unref(o); - - pa_stream_disconnect(stream); - pa_stream_unref(stream); - stream = NULL; - - pa_threaded_mainloop_unlock(mainloop); - } -} - -void radio_output_suspend(int state) -{ - if(stream) { - pa_stream_cork(stream, state, NULL, NULL); - } -} - -void radio_output_close(void) -{ - radio_output_stop(); - - if(context) { - pa_context_disconnect(context); - pa_context_unref(context); - context = NULL; - } - - if(mainloop) { - pa_threaded_mainloop_stop(mainloop); - pa_threaded_mainloop_free(mainloop); - mainloop = NULL; - } - - free(output_buf); - output_buf = NULL; -} - -int radio_output_write(void *buf, int len) -{ - int rc = -EINVAL; - int error; - size_t n = len; - size_t avail; - int samples = len / 2; - void *p; - - if(!stream) { - return -1; - } - - if(!buf) { - fprintf(stderr, "Error: buf == null!\n"); - return rc; - } - - pa_threaded_mainloop_lock(mainloop); - - avail = pa_stream_writable_size(stream); - if(avail < n) { - /* - * NOTE: Definitely room for improvement here,but for now just - * check for the no space case that happens when the - * stream is corked. - */ - if(!avail) { - rc = 0; - goto exit; - } - } - - /* - * Handle the rtl_fm code giving us an odd number of samples, which - * PA does not like. This extra buffer copying approach is not - * particularly efficient, but works for now. It looks feasible to - * hack in something in the demod and output thread routines in - * rtl_fm.c to handle it there if more performance is required. - */ - p = output_buf; - if(extra) { - memcpy(output_buf, extra_buf, sizeof(int16_t)); - if((extra + samples) % 2) { - // We still have an extra sample, n remains the same, store the extra - memcpy(output_buf + sizeof(int16_t), buf, n - 2); - memcpy(extra_buf, ((unsigned char*) buf) + n - 2, sizeof(int16_t)); - } else { - // We have an even number of samples, no extra - memcpy(output_buf + sizeof(int16_t), buf, n); - n += 2; - extra = 0; - } - } else if(samples % 2) { - // We have an extra sample, store it, and decrease n - n -= 2; - memcpy(output_buf + sizeof(int16_t), buf, n); - memcpy(extra_buf, ((unsigned char*) buf) + n, sizeof(int16_t)); - extra = 1; - } else { - p = buf; - } - - if ((rc = pa_stream_write(stream, p, n, NULL, 0, PA_SEEK_RELATIVE)) < 0) { - fprintf(stderr, "Error writing %d bytes to PulseAudio : %s\n", - n, pa_strerror(pa_context_errno(context))); - } -exit: - pa_threaded_mainloop_unlock(mainloop); - - return rc; -} diff --git a/binding/radio_output.h b/binding/radio_output.h deleted file mode 100644 index 2192811..0000000 --- a/binding/radio_output.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2017 Konsulko Group - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _RADIO_OUTPUT_H -#define _RADIO_OUTPUT_H - -int radio_output_open(void); - -int radio_output_start(void); - -void radio_output_stop(void); - -void radio_output_close(void); - -int radio_output_write(void *buf, int len); - -#endif /* _RADIO_OUTPUT_H */ - diff --git a/binding/rtl_fm.c b/binding/rtl_fm.c deleted file mode 100644 index 1c6a6b2..0000000 --- a/binding/rtl_fm.c +++ /dev/null @@ -1,1267 +0,0 @@ -/* - * rtl-sdr, turns your Realtek RTL2832 based DVB dongle into a SDR receiver - * Copyright (C) 2012 by Steve Markgraf <steve@steve-m.de> - * Copyright (C) 2012 by Hoernchen <la@tfc-server.de> - * Copyright (C) 2012 by Kyle Keen <keenerd@gmail.com> - * Copyright (C) 2013 by Elias Oenal <EliasOenal@gmail.com> - * Copyright (C) 2016, 2017 Konsulko Group - * - * 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/>. - */ - -/* - * Note that this version replaces the standalone main() with separate - * init/start/stop API calls to allow building into another application. - * Other than removing the separate controller thread and adding an output - * function callback, other changes have been kept to a minimum to - * potentially allow using other rtl_fm features by modifying rtl_fm_init. - * - * December 2016, Scott Murray <scott.murray@konsulko.com> - */ - -/* - * written because people could not do real time - * FM demod on Atom hardware with GNU radio - * based on rtl_sdr.c and rtl_tcp.c - * - * lots of locks, but that is okay - * (no many-to-many locks) - * - * todo: - * sanity checks - * scale squelch to other input parameters - * test all the demodulations - * pad output on hop - * frequency ranges could be stored better - * scaled AM demod amplification - * auto-hop after time limit - * peak detector to tune onto stronger signals - * fifo for active hop frequency - * clips - * noise squelch - * merge stereo patch - * merge soft agc patch - * merge udp patch - * testmode to detect overruns - * watchdog to reset bad dongle - * fix oversampling - */ - -#include <errno.h> -#include <signal.h> -#include <string.h> -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <math.h> -#include <pthread.h> - -#include "rtl-sdr.h" -#include "rtl_fm.h" -#include "convenience/convenience.h" - -#define DEFAULT_SAMPLE_RATE 24000 -#define DEFAULT_BUF_LENGTH RTL_FM_DEFAULT_BUF_LENGTH -#define MAXIMUM_OVERSAMPLE RTL_FM_MAXIMUM_OVERSAMPLE -#define MAXIMUM_BUF_LENGTH RTL_FM_MAXIMUM_BUF_LENGTH -#define AUTO_GAIN -100 -#define BUFFER_DUMP 4096 - -#define FREQUENCIES_LIMIT 1000 - -#define DEFAULT_SQUELCH_LEVEL 140 -#define DEFAULT_CONSEQ_SQUELCH 10 - -static volatile int do_exit = 0; -static int lcm_post[17] = {1,1,1,3,1,5,3,7,1,9,5,11,3,13,7,15,1}; -static int ACTUAL_BUF_LENGTH; - -static int *atan_lut = NULL; -static int atan_lut_size = 131072; /* 512 KB */ -static int atan_lut_coef = 8; - -struct dongle_state -{ - int exit_flag; - pthread_t thread; - rtlsdr_dev_t *dev; - int dev_index; - uint32_t freq; - uint32_t rate; - int gain; - uint16_t buf16[MAXIMUM_BUF_LENGTH]; - uint32_t buf_len; - int ppm_error; - int offset_tuning; - int direct_sampling; - int mute; - struct demod_state *demod_target; -}; - -struct demod_state -{ - int exit_flag; - pthread_t thread; - int16_t lowpassed[MAXIMUM_BUF_LENGTH]; - int lp_len; - int16_t lp_i_hist[10][6]; - int16_t lp_q_hist[10][6]; - int16_t result[MAXIMUM_BUF_LENGTH]; - int16_t droop_i_hist[9]; - int16_t droop_q_hist[9]; - int result_len; - int rate_in; - int rate_out; - int rate_out2; - int now_r, now_j; - int pre_r, pre_j; - int prev_index; - int downsample; /* min 1, max 256 */ - int post_downsample; - int output_scale; - int squelch_level, conseq_squelch, squelch_hits, terminate_on_squelch; - int downsample_passes; - int comp_fir_size; - int custom_atan; - int deemph, deemph_a; - int now_lpr; - int prev_lpr_index; - int dc_block, dc_avg; - void (*mode_demod)(struct demod_state*); - pthread_rwlock_t rw; - pthread_cond_t ready; - pthread_mutex_t ready_m; - struct output_state *output_target; -}; - -struct output_state -{ - int exit_flag; - pthread_t thread; - rtl_fm_output_fn_t output_fn; - void *output_fn_data; - int16_t result[MAXIMUM_BUF_LENGTH]; - int result_len; - int rate; - pthread_rwlock_t rw; - pthread_cond_t ready; - pthread_mutex_t ready_m; -}; - -struct controller_state -{ - int exit_flag; - pthread_t thread; - uint32_t freqs[FREQUENCIES_LIMIT]; - int freq_len; - int freq_now; - int edge; - int wb_mode; - pthread_cond_t hop; - pthread_mutex_t hop_m; - - void (*freq_callback)(uint32_t, void*); - void *freq_callback_data; - - int scanning; - int scan_direction; - void (*scan_callback)(uint32_t, void*); - void *scan_callback_data; - uint32_t scan_step; - uint32_t scan_min; - uint32_t scan_max; - int scan_squelch_level; - int scan_squelch_count; -}; - -// multiple of these, eventually -struct dongle_state dongle; -struct demod_state demod; -struct output_state output; -struct controller_state controller; - -#if 0 -static void sighandler(int signum) -{ - fprintf(stderr, "Signal caught, exiting!\n"); - do_exit = 1; - rtlsdr_cancel_async(dongle.dev); -} -#endif - -/* more cond dumbness */ -#define safe_cond_signal(n, m) pthread_mutex_lock(m); pthread_cond_signal(n); pthread_mutex_unlock(m) -#define safe_cond_wait(n, m) pthread_mutex_lock(m); pthread_cond_wait(n, m); pthread_mutex_unlock(m) - -/* {length, coef, coef, coef} and scaled by 2^15 - for now, only length 9, optimal way to get +85% bandwidth */ -#define CIC_TABLE_MAX 10 -int cic_9_tables[][10] = { - {0,}, - {9, -156, -97, 2798, -15489, 61019, -15489, 2798, -97, -156}, - {9, -128, -568, 5593, -24125, 74126, -24125, 5593, -568, -128}, - {9, -129, -639, 6187, -26281, 77511, -26281, 6187, -639, -129}, - {9, -122, -612, 6082, -26353, 77818, -26353, 6082, -612, -122}, - {9, -120, -602, 6015, -26269, 77757, -26269, 6015, -602, -120}, - {9, -120, -582, 5951, -26128, 77542, -26128, 5951, -582, -120}, - {9, -119, -580, 5931, -26094, 77505, -26094, 5931, -580, -119}, - {9, -119, -578, 5921, -26077, 77484, -26077, 5921, -578, -119}, - {9, -119, -577, 5917, -26067, 77473, -26067, 5917, -577, -119}, - {9, -199, -362, 5303, -25505, 77489, -25505, 5303, -362, -199}, -}; - -void rotate_90(unsigned char *buf, uint32_t len) -/* 90 rotation is 1+0j, 0+1j, -1+0j, 0-1j - or [0, 1, -3, 2, -4, -5, 7, -6] */ -{ - uint32_t i; - unsigned char tmp; - for (i=0; i<len; i+=8) { - /* uint8_t negation = 255 - x */ - tmp = 255 - buf[i+3]; - buf[i+3] = buf[i+2]; - buf[i+2] = tmp; - - buf[i+4] = 255 - buf[i+4]; - buf[i+5] = 255 - buf[i+5]; - - tmp = 255 - buf[i+6]; - buf[i+6] = buf[i+7]; - buf[i+7] = tmp; - } -} - -void low_pass(struct demod_state *d) -/* simple square window FIR */ -{ - int i=0, i2=0; - while (i < d->lp_len) { - d->now_r += d->lowpassed[i]; - d->now_j += d->lowpassed[i+1]; - i += 2; - d->prev_index++; - if (d->prev_index < d->downsample) { - continue; - } - d->lowpassed[i2] = d->now_r; // * d->output_scale; - d->lowpassed[i2+1] = d->now_j; // * d->output_scale; - d->prev_index = 0; - d->now_r = 0; - d->now_j = 0; - i2 += 2; - } - d->lp_len = i2; -} - -int low_pass_simple(int16_t *signal2, int len, int step) -// no wrap around, length must be multiple of step -{ - int i, i2, sum; - for(i=0; i < len; i+=step) { - sum = 0; - for(i2=0; i2<step; i2++) { - sum += (int)signal2[i + i2]; - } - //signal2[i/step] = (int16_t)(sum / step); - signal2[i/step] = (int16_t)(sum); - } - signal2[i/step + 1] = signal2[i/step]; - return len / step; -} - -void low_pass_real(struct demod_state *s) -/* simple square window FIR */ -// add support for upsampling? -{ - int i=0, i2=0; - int fast = (int)s->rate_out; - int slow = s->rate_out2; - while (i < s->result_len) { - s->now_lpr += s->result[i]; - i++; - s->prev_lpr_index += slow; - if (s->prev_lpr_index < fast) { - continue; - } - s->result[i2] = (int16_t)(s->now_lpr / (fast/slow)); - s->prev_lpr_index -= fast; - s->now_lpr = 0; - i2 += 1; - } - s->result_len = i2; -} - -void fifth_order(int16_t *data, int length, int16_t *hist) -/* for half of interleaved data */ -{ - int i; - int16_t a, b, c, d, e, f; - a = hist[1]; - b = hist[2]; - c = hist[3]; - d = hist[4]; - e = hist[5]; - f = data[0]; - /* a downsample should improve resolution, so don't fully shift */ - data[0] = (a + (b+e)*5 + (c+d)*10 + f) >> 4; - for (i=4; i<length; i+=4) { - a = c; - b = d; - c = e; - d = f; - e = data[i-2]; - f = data[i]; - data[i/2] = (a + (b+e)*5 + (c+d)*10 + f) >> 4; - } - /* archive */ - hist[0] = a; - hist[1] = b; - hist[2] = c; - hist[3] = d; - hist[4] = e; - hist[5] = f; -} - -void generic_fir(int16_t *data, int length, int *fir, int16_t *hist) -/* Okay, not at all generic. Assumes length 9, fix that eventually. */ -{ - int d, temp, sum; - for (d=0; d<length; d+=2) { - temp = data[d]; - sum = 0; - sum += (hist[0] + hist[8]) * fir[1]; - sum += (hist[1] + hist[7]) * fir[2]; - sum += (hist[2] + hist[6]) * fir[3]; - sum += (hist[3] + hist[5]) * fir[4]; - sum += hist[4] * fir[5]; - data[d] = sum >> 15 ; - hist[0] = hist[1]; - hist[1] = hist[2]; - hist[2] = hist[3]; - hist[3] = hist[4]; - hist[4] = hist[5]; - hist[5] = hist[6]; - hist[6] = hist[7]; - hist[7] = hist[8]; - hist[8] = temp; - } -} - -/* define our own complex math ops - because ARMv5 has no hardware float */ - -void multiply(int ar, int aj, int br, int bj, int *cr, int *cj) -{ - *cr = ar*br - aj*bj; - *cj = aj*br + ar*bj; -} - -int polar_discriminant(int ar, int aj, int br, int bj) -{ - int cr, cj; - double angle; - multiply(ar, aj, br, -bj, &cr, &cj); - angle = atan2((double)cj, (double)cr); - return (int)(angle / 3.14159 * (1<<14)); -} - -int fast_atan2(int y, int x) -/* pre scaled for int16 */ -{ - int yabs, angle; - int pi4=(1<<12), pi34=3*(1<<12); // note pi = 1<<14 - if (x==0 && y==0) { - return 0; - } - yabs = y; - if (yabs < 0) { - yabs = -yabs; - } - if (x >= 0) { - angle = pi4 - pi4 * (x-yabs) / (x+yabs); - } else { - angle = pi34 - pi4 * (x+yabs) / (yabs-x); - } - if (y < 0) { - return -angle; - } - return angle; -} - -int polar_disc_fast(int ar, int aj, int br, int bj) -{ - int cr, cj; - multiply(ar, aj, br, -bj, &cr, &cj); - return fast_atan2(cj, cr); -} - -int atan_lut_init(void) -{ - int i = 0; - - atan_lut = malloc(atan_lut_size * sizeof(int)); - - for (i = 0; i < atan_lut_size; i++) { - atan_lut[i] = (int) (atan((double) i / (1<<atan_lut_coef)) / 3.14159 * (1<<14)); - } - - return 0; -} - -int polar_disc_lut(int ar, int aj, int br, int bj) -{ - int cr, cj, x, x_abs; - - multiply(ar, aj, br, -bj, &cr, &cj); - - /* special cases */ - if (cr == 0 || cj == 0) { - if (cr == 0 && cj == 0) - {return 0;} - if (cr == 0 && cj > 0) - {return 1 << 13;} - if (cr == 0 && cj < 0) - {return -(1 << 13);} - if (cj == 0 && cr > 0) - {return 0;} - if (cj == 0 && cr < 0) - {return 1 << 14;} - } - - /* real range -32768 - 32768 use 64x range -> absolute maximum: 2097152 */ - x = (cj << atan_lut_coef) / cr; - x_abs = abs(x); - - if (x_abs >= atan_lut_size) { - /* we can use linear range, but it is not necessary */ - return (cj > 0) ? 1<<13 : -1<<13; - } - - if (x > 0) { - return (cj > 0) ? atan_lut[x] : atan_lut[x] - (1<<14); - } else { - return (cj > 0) ? (1<<14) - atan_lut[-x] : -atan_lut[-x]; - } - - return 0; -} - -void fm_demod(struct demod_state *fm) -{ - int i, pcm; - int16_t *lp = fm->lowpassed; - pcm = polar_discriminant(lp[0], lp[1], - fm->pre_r, fm->pre_j); - fm->result[0] = (int16_t)pcm; - for (i = 2; i < (fm->lp_len-1); i += 2) { - switch (fm->custom_atan) { - case 0: - pcm = polar_discriminant(lp[i], lp[i+1], - lp[i-2], lp[i-1]); - break; - case 1: - pcm = polar_disc_fast(lp[i], lp[i+1], - lp[i-2], lp[i-1]); - break; - case 2: - pcm = polar_disc_lut(lp[i], lp[i+1], - lp[i-2], lp[i-1]); - break; - } - fm->result[i/2] = (int16_t)pcm; - } - fm->pre_r = lp[fm->lp_len - 2]; - fm->pre_j = lp[fm->lp_len - 1]; - fm->result_len = fm->lp_len/2; -} - -void am_demod(struct demod_state *fm) -// todo, fix this extreme laziness -{ - int i, pcm; - int16_t *lp = fm->lowpassed; - int16_t *r = fm->result; - for (i = 0; i < fm->lp_len; i += 2) { - // hypot uses floats but won't overflow - //r[i/2] = (int16_t)hypot(lp[i], lp[i+1]); - pcm = lp[i] * lp[i]; - pcm += lp[i+1] * lp[i+1]; - r[i/2] = (int16_t)sqrt(pcm) * fm->output_scale; - } - fm->result_len = fm->lp_len/2; - // lowpass? (3khz) highpass? (dc) -} - -void usb_demod(struct demod_state *fm) -{ - int i, pcm; - int16_t *lp = fm->lowpassed; - int16_t *r = fm->result; - for (i = 0; i < fm->lp_len; i += 2) { - pcm = lp[i] + lp[i+1]; - r[i/2] = (int16_t)pcm * fm->output_scale; - } - fm->result_len = fm->lp_len/2; -} - -void lsb_demod(struct demod_state *fm) -{ - int i, pcm; - int16_t *lp = fm->lowpassed; - int16_t *r = fm->result; - for (i = 0; i < fm->lp_len; i += 2) { - pcm = lp[i] - lp[i+1]; - r[i/2] = (int16_t)pcm * fm->output_scale; - } - fm->result_len = fm->lp_len/2; -} - -void raw_demod(struct demod_state *fm) -{ - int i; - for (i = 0; i < fm->lp_len; i++) { - fm->result[i] = (int16_t)fm->lowpassed[i]; - } - fm->result_len = fm->lp_len; -} - -void deemph_filter(struct demod_state *fm) -{ - static int avg; // cheating... - int i, d; - // de-emph IIR - // avg = avg * (1 - alpha) + sample * alpha; - for (i = 0; i < fm->result_len; i++) { - d = fm->result[i] - avg; - if (d > 0) { - avg += (d + fm->deemph_a/2) / fm->deemph_a; - } else { - avg += (d - fm->deemph_a/2) / fm->deemph_a; - } - fm->result[i] = (int16_t)avg; - } -} - -void dc_block_filter(struct demod_state *fm) -{ - int i, avg; - int64_t sum = 0; - for (i=0; i < fm->result_len; i++) { - sum += fm->result[i]; - } - avg = sum / fm->result_len; - avg = (avg + fm->dc_avg * 9) / 10; - for (i=0; i < fm->result_len; i++) { - fm->result[i] -= avg; - } - fm->dc_avg = avg; -} - -int mad(int16_t *samples, int len, int step) -/* mean average deviation */ -{ - int i=0, sum=0, ave=0; - if (len == 0) - {return 0;} - for (i=0; i<len; i+=step) { - sum += samples[i]; - } - ave = sum / (len * step); - sum = 0; - for (i=0; i<len; i+=step) { - sum += abs(samples[i] - ave); - } - return sum / (len / step); -} - -int rms(int16_t *samples, int len, int step) -/* largely lifted from rtl_power */ -{ - int i; - long p, t, s; - double dc, err; - - p = t = 0L; - for (i=0; i<len; i+=step) { - s = (long)samples[i]; - t += s; - p += s * s; - } - /* correct for dc offset in squares */ - dc = (double)(t*step) / (double)len; - err = t * 2 * dc - dc * dc * len; - - return (int)sqrt((p-err) / len); -} - -void arbitrary_upsample(int16_t *buf1, int16_t *buf2, int len1, int len2) -/* linear interpolation, len1 < len2 */ -{ - int i = 1; - int j = 0; - int tick = 0; - double frac; // use integers... - while (j < len2) { - frac = (double)tick / (double)len2; - buf2[j] = (int16_t)(buf1[i-1]*(1-frac) + buf1[i]*frac); - j++; - tick += len1; - if (tick > len2) { - tick -= len2; - i++; - } - if (i >= len1) { - i = len1 - 1; - tick = len2; - } - } -} - -void arbitrary_downsample(int16_t *buf1, int16_t *buf2, int len1, int len2) -/* fractional boxcar lowpass, len1 > len2 */ -{ - int i = 1; - int j = 0; - int tick = 0; - double remainder = 0; - double frac; // use integers... - buf2[0] = 0; - while (j < len2) { - frac = 1.0; - if ((tick + len2) > len1) { - frac = (double)(len1 - tick) / (double)len2;} - buf2[j] += (int16_t)((double)buf1[i] * frac + remainder); - remainder = (double)buf1[i] * (1.0-frac); - tick += len2; - i++; - if (tick > len1) { - j++; - buf2[j] = 0; - tick -= len1; - } - if (i >= len1) { - i = len1 - 1; - tick = len1; - } - } - for (j=0; j<len2; j++) { - buf2[j] = buf2[j] * len2 / len1;} -} - -void arbitrary_resample(int16_t *buf1, int16_t *buf2, int len1, int len2) -/* up to you to calculate lengths and make sure it does not go OOB - * okay for buffers to overlap, if you are downsampling */ -{ - if (len1 < len2) { - arbitrary_upsample(buf1, buf2, len1, len2); - } else { - arbitrary_downsample(buf1, buf2, len1, len2); - } -} - -void full_demod(struct demod_state *d) -{ - int i, ds_p; - int sr = 0; - ds_p = d->downsample_passes; - if (ds_p) { - for (i=0; i < ds_p; i++) { - fifth_order(d->lowpassed, (d->lp_len >> i), d->lp_i_hist[i]); - fifth_order(d->lowpassed+1, (d->lp_len >> i) - 1, d->lp_q_hist[i]); - } - d->lp_len = d->lp_len >> ds_p; - /* droop compensation */ - if (d->comp_fir_size == 9 && ds_p <= CIC_TABLE_MAX) { - generic_fir(d->lowpassed, d->lp_len, - cic_9_tables[ds_p], d->droop_i_hist); - generic_fir(d->lowpassed+1, d->lp_len-1, - cic_9_tables[ds_p], d->droop_q_hist); - } - } else { - low_pass(d); - } - /* power squelch */ - if (d->squelch_level) { - sr = rms(d->lowpassed, d->lp_len, 1); - if (sr < d->squelch_level) { - d->squelch_hits++; - for (i=0; i< d->lp_len; i++) { - d->lowpassed[i] = 0; - } - } else { - d->squelch_hits = 0; - } - } - d->mode_demod(d); /* lowpassed -> result */ - if (d->mode_demod == &raw_demod) { - return; - } - /* todo, fm noise squelch */ - // use nicer filter here too? - if (d->post_downsample > 1) { - d->result_len = low_pass_simple(d->result, d->result_len, d->post_downsample);} - if (d->deemph) { - deemph_filter(d);} - if (d->dc_block) { - dc_block_filter(d);} - if (d->rate_out2 > 0) { - low_pass_real(d); - //arbitrary_resample(d->result, d->result, d->result_len, d->result_len * d->rate_out2 / d->rate_out); - } -} - -static void rtlsdr_callback(unsigned char *buf, uint32_t len, void *ctx) -{ - int i; - struct dongle_state *s = ctx; - struct demod_state *d = s->demod_target; - - if (do_exit) { - return;} - if (!ctx) { - return;} - if (s->mute) { - for (i=0; i<s->mute; i++) { - buf[i] = 127;} - s->mute = 0; - } - if (!s->offset_tuning) { - rotate_90(buf, len);} - for (i=0; i<(int)len; i++) { - s->buf16[i] = (int16_t)buf[i] - 127;} - pthread_rwlock_wrlock(&d->rw); - memcpy(d->lowpassed, s->buf16, 2*len); - d->lp_len = len; - pthread_rwlock_unlock(&d->rw); - safe_cond_signal(&d->ready, &d->ready_m); -} - -static void *dongle_thread_fn(void *arg) -{ - struct dongle_state *s = arg; - fprintf(stderr, "dongle_thread_fn running\n"); - rtlsdr_read_async(s->dev, rtlsdr_callback, s, 0, s->buf_len); - fprintf(stderr, "dongle_thread_fn exited!\n"); - return 0; -} - -static void rtl_fm_scan_callback(void) -{ - struct controller_state *s = &controller; - uint32_t frequency = rtl_fm_get_freq(); - - if(!s->scanning) - return; - - if(!s->scan_direction) { - frequency += s->scan_step; - if(frequency > s->scan_max) - frequency = s->scan_min; - } else { - frequency -= s->scan_step; - if(frequency < s->scan_min) - frequency = s->scan_max; - } - - rtl_fm_set_freq(frequency); -} - -static void rtl_fm_scan_end_callback(void) -{ - struct controller_state *s = &controller; - - if(!s->scanning) - return; - - rtl_fm_scan_stop(); - - if(s->scan_callback) - s->scan_callback(rtl_fm_get_freq(), s->scan_callback_data); -} - -static void *demod_thread_fn(void *arg) -{ - struct demod_state *d = arg; - struct output_state *o = d->output_target; - fprintf(stderr, "demod_thread_fn running\n"); - while (!do_exit) { - safe_cond_wait(&d->ready, &d->ready_m); - pthread_rwlock_wrlock(&d->rw); - full_demod(d); - pthread_rwlock_unlock(&d->rw); - if (d->exit_flag) { - do_exit = 1; - } - if (d->squelch_level) { - if(d->squelch_hits > d->conseq_squelch) { - d->squelch_hits = d->conseq_squelch + 1; /* hair trigger */ - //safe_cond_signal(&controller.hop, &controller.hop_m); - rtl_fm_scan_callback(); - continue; - } else if(!d->squelch_hits) { - rtl_fm_scan_end_callback(); - } - } - pthread_rwlock_wrlock(&o->rw); - memcpy(o->result, d->result, 2*d->result_len); - o->result_len = d->result_len; - pthread_rwlock_unlock(&o->rw); - safe_cond_signal(&o->ready, &o->ready_m); - } - fprintf(stderr, "demod_thread_fn exited!\n"); - return 0; -} - -static void *output_thread_fn(void *arg) -{ - struct output_state *s = arg; - fprintf(stderr, "output_thread_fn running\n"); - while (!do_exit) { - // use timedwait and pad out under runs - safe_cond_wait(&s->ready, &s->ready_m); - pthread_rwlock_rdlock(&s->rw); - if(s->output_fn) { - s->output_fn(s->result, s->result_len, s->output_fn_data); - } - pthread_rwlock_unlock(&s->rw); - } - fprintf(stderr, "output_thread_fn exited!\n"); - return 0; -} - -static void optimal_settings(int freq, int rate) -{ - // giant ball of hacks - // seems unable to do a single pass, 2:1 - int capture_freq, capture_rate; - struct dongle_state *d = &dongle; - struct demod_state *dm = &demod; - struct controller_state *cs = &controller; - dm->downsample = (1000000 / dm->rate_in) + 1; - if (dm->downsample_passes) { - dm->downsample_passes = (int)log2(dm->downsample) + 1; - dm->downsample = 1 << dm->downsample_passes; - } - capture_freq = freq; - capture_rate = dm->downsample * dm->rate_in; - if (!d->offset_tuning) { - capture_freq = freq + capture_rate/4;} - capture_freq += cs->edge * dm->rate_in / 2; - dm->output_scale = (1<<15) / (128 * dm->downsample); - if (dm->output_scale < 1) { - dm->output_scale = 1;} - if (dm->mode_demod == &fm_demod) { - dm->output_scale = 1;} - d->freq = (uint32_t)capture_freq; - d->rate = (uint32_t)capture_rate; -} - - -void frequency_range(struct controller_state *s, char *arg) -{ - char *start, *stop, *step; - int i; - start = arg; - stop = strchr(start, ':') + 1; - stop[-1] = '\0'; - step = strchr(stop, ':') + 1; - step[-1] = '\0'; - for(i=(int)atofs(start); i<=(int)atofs(stop); i+=(int)atofs(step)) - { - s->freqs[s->freq_len] = (uint32_t)i; - s->freq_len++; - if (s->freq_len >= FREQUENCIES_LIMIT) { - break;} - } - stop[-1] = ':'; - step[-1] = ':'; -} - -void dongle_init(struct dongle_state *s) -{ - s->rate = DEFAULT_SAMPLE_RATE; - s->gain = AUTO_GAIN; // tenths of a dB - s->mute = 0; - s->direct_sampling = 0; - s->offset_tuning = 0; - s->demod_target = &demod; -} - -void demod_init(struct demod_state *s) -{ - s->rate_in = DEFAULT_SAMPLE_RATE; - s->rate_out = DEFAULT_SAMPLE_RATE; - s->squelch_level = 0; - s->conseq_squelch = DEFAULT_CONSEQ_SQUELCH; - s->terminate_on_squelch = 0; - s->squelch_hits = DEFAULT_CONSEQ_SQUELCH + 1; - s->downsample_passes = 0; - s->comp_fir_size = 0; - s->prev_index = 0; - s->post_downsample = 1; // once this works, default = 4 - s->custom_atan = 0; - s->deemph = 0; - s->rate_out2 = -1; // flag for disabled - s->mode_demod = &fm_demod; - s->pre_j = s->pre_r = s->now_r = s->now_j = 0; - s->prev_lpr_index = 0; - s->deemph_a = 0; - s->now_lpr = 0; - s->dc_block = 0; - s->dc_avg = 0; - pthread_rwlock_init(&s->rw, NULL); - pthread_cond_init(&s->ready, NULL); - pthread_mutex_init(&s->ready_m, NULL); - s->output_target = &output; -} - -void demod_cleanup(struct demod_state *s) -{ - pthread_rwlock_destroy(&s->rw); - pthread_cond_destroy(&s->ready); - pthread_mutex_destroy(&s->ready_m); -} - -void output_init(struct output_state *s) -{ - s->rate = DEFAULT_SAMPLE_RATE; - s->output_fn = NULL; - s->output_fn_data = NULL; - pthread_rwlock_init(&s->rw, NULL); - pthread_cond_init(&s->ready, NULL); - pthread_mutex_init(&s->ready_m, NULL); -} - -void output_cleanup(struct output_state *s) -{ - pthread_rwlock_destroy(&s->rw); - pthread_cond_destroy(&s->ready); - pthread_mutex_destroy(&s->ready_m); -} - -void controller_init(struct controller_state *s) -{ - s->freqs[0] = 100000000; - s->freq_len = 0; - s->edge = 0; - s->wb_mode = 0; - pthread_cond_init(&s->hop, NULL); - pthread_mutex_init(&s->hop_m, NULL); -} - -void controller_cleanup(struct controller_state *s) -{ - pthread_cond_destroy(&s->hop); - pthread_mutex_destroy(&s->hop_m); -} - -void sanity_checks(void) -{ - if (controller.freq_len == 0) { - fprintf(stderr, "Please specify a frequency.\n"); - exit(1); - } - - if (controller.freq_len >= FREQUENCIES_LIMIT) { - fprintf(stderr, "Too many channels, maximum %i.\n", FREQUENCIES_LIMIT); - exit(1); - } - - if (controller.freq_len > 1 && demod.squelch_level == 0) { - fprintf(stderr, "Please specify a squelch level. Required for scanning multiple frequencies.\n"); - exit(1); - } - -} - -int rtl_fm_init(uint32_t freq, - uint32_t sample_rate, - uint32_t resample_rate, - rtl_fm_output_fn_t output_fn, - void *output_fn_data) -{ - int r = 0; - - dongle_init(&dongle); - demod_init(&demod); - output_init(&output); - controller_init(&controller); - - /* - * Simulate the effects of command line arguments: - * - * -W wbfm -s <sample rate> -r <resample rate> - */ - - /* Set initial frequency */ - controller.freqs[0] = freq; - controller.freq_len++; - - /* Set mode to wbfm */ - controller.wb_mode = 1; - demod.mode_demod = &fm_demod; - demod.rate_in = 170000; - demod.rate_out = 170000; - demod.rate_out2 = 32000; - demod.custom_atan = 1; - //demod.post_downsample = 4; - demod.deemph = 1; - controller.scan_squelch_count = DEFAULT_CONSEQ_SQUELCH; - controller.scan_squelch_level = DEFAULT_SQUELCH_LEVEL; - demod.squelch_level = 0; - - /* Adjust frequency for wb mode */ - controller.freqs[0] += 16000; - - /* Set sample rate */ - demod.rate_in = sample_rate; - demod.rate_out = sample_rate; - - /* Set resample rate */ - output.rate = (int) resample_rate; - demod.rate_out2 = (int) resample_rate; - - /* Set output function pointer */ - if(output_fn) { - output.output_fn = output_fn; - output.output_fn_data = output_fn_data; - } - - /* quadruple sample_rate to limit to Δθ to ±π/2 */ - demod.rate_in *= demod.post_downsample; - - if (!output.rate) { - output.rate = demod.rate_out; - } - - sanity_checks(); - - if (controller.freq_len > 1) { - demod.terminate_on_squelch = 0; - } - - ACTUAL_BUF_LENGTH = lcm_post[demod.post_downsample] * DEFAULT_BUF_LENGTH; - - dongle.dev_index = verbose_device_search("0"); - if (dongle.dev_index < 0) { - return -1; - } - - r = rtlsdr_open(&dongle.dev, (uint32_t)dongle.dev_index); - if (r < 0) { - fprintf(stderr, "Failed to open rtlsdr device #%d.\n", dongle.dev_index); - return r; - } - - if (demod.deemph) { - demod.deemph_a = (int)round(1.0/((1.0-exp(-1.0/(demod.rate_out * 75e-6))))); - } - - /* Set the tuner gain */ - if (dongle.gain == AUTO_GAIN) { - verbose_auto_gain(dongle.dev); - } else { - dongle.gain = nearest_gain(dongle.dev, dongle.gain); - verbose_gain_set(dongle.dev, dongle.gain); - } - - verbose_ppm_set(dongle.dev, dongle.ppm_error); - - //r = rtlsdr_set_testmode(dongle.dev, 1); - - return r; -} - -void rtl_fm_start(void) -{ - struct controller_state *s = &controller; - - /* - * A bunch of the following is pulled from the controller_thread_fn, - * which has been removed. - */ - - /* Reset endpoint before we start reading from it (mandatory) */ - verbose_reset_buffer(dongle.dev); - - /* set up primary channel */ - optimal_settings(s->freqs[0], demod.rate_in); - if (dongle.direct_sampling) { - verbose_direct_sampling(dongle.dev, 1);} - if (dongle.offset_tuning) { - verbose_offset_tuning(dongle.dev);} - - /* Set the frequency */ - verbose_set_frequency(dongle.dev, dongle.freq); - fprintf(stderr, "Oversampling input by: %ix.\n", demod.downsample); - fprintf(stderr, "Oversampling output by: %ix.\n", demod.post_downsample); - fprintf(stderr, "Buffer size: %0.2fms\n", - 1000 * 0.5 * (float)ACTUAL_BUF_LENGTH / (float)dongle.rate); - - /* Set the sample rate */ - verbose_set_sample_rate(dongle.dev, dongle.rate); - fprintf(stderr, "Output at %u Hz.\n", demod.rate_in/demod.post_downsample); - usleep(100000); - - rtl_fm_scan_stop(); - - do_exit = 0; - pthread_create(&output.thread, NULL, output_thread_fn, (void *)(&output)); - pthread_create(&demod.thread, NULL, demod_thread_fn, (void *)(&demod)); - pthread_create(&dongle.thread, NULL, dongle_thread_fn, (void *)(&dongle)); -} - -void rtl_fm_set_freq(uint32_t freq) -{ - struct controller_state *s = &controller; - - if(s->freqs[0] == freq) - return; - - s->freqs[0] = freq; - s->freq_len = 1; - - if (s->wb_mode) { - s->freqs[0] += 16000; - } - - optimal_settings(s->freqs[0], demod.rate_in); - if (dongle.offset_tuning) { - verbose_offset_tuning(dongle.dev); - } - rtlsdr_set_center_freq(dongle.dev, dongle.freq); - - // It does not look like refreshing the sample rate is desirable - // (e.g. the scanning code in the removed controller thread function - // did not do it), and behavior seemed a bit less robust with it - // present. However, I am leaving this here as a reminder to revisit - // via some more testing. - //rtlsdr_set_sample_rate(dongle.dev, dongle.rate); - - // This triggers a mute during the frequency change - dongle.mute = BUFFER_DUMP; - - if(s->freq_callback) - s->freq_callback(freq, s->freq_callback_data); -} - -void rtl_fm_set_freq_callback(void (*callback)(uint32_t, void *), - void *data) -{ - struct controller_state *s = &controller; - - s->freq_callback = callback; - s->freq_callback_data = data; -} - -uint32_t rtl_fm_get_freq(void) -{ - struct controller_state *s = &controller; - uint32_t frequency = s->freqs[0]; - - if (s->wb_mode) - frequency -= 16000; - - return frequency; -} - -void rtl_fm_stop(void) -{ - rtl_fm_scan_stop(); - - rtlsdr_cancel_async(dongle.dev); - do_exit = 1; - pthread_join(dongle.thread, NULL); - safe_cond_signal(&demod.ready, &demod.ready_m); - pthread_join(demod.thread, NULL); - safe_cond_signal(&output.ready, &output.ready_m); - pthread_join(output.thread, NULL); -} - -void rtl_fm_scan_start(int direction, - void (*callback)(uint32_t, void *), - void *data, - uint32_t step, - uint32_t min, - uint32_t max) -{ - struct controller_state *s = &controller; - struct demod_state *dm = &demod; - uint32_t frequency = rtl_fm_get_freq(); - - if(s->scanning && s->scan_direction == direction) - return; - - s->scanning = 1; - s->scan_direction = direction; - s->scan_callback = callback; - s->scan_callback_data = data; - s->scan_step = step; - s->scan_min = min; - s->scan_max = max; - - /* Start scan by stepping in the desired direction */ - if(!direction) { - frequency += s->scan_step; - if(frequency > s->scan_max) - frequency = s->scan_min; - } else { - frequency -= s->scan_step; - if(frequency < s->scan_min) - frequency = s->scan_max; - } - - rtl_fm_set_freq(frequency); - - dm->conseq_squelch = s->scan_squelch_count; - dm->squelch_hits = s->scan_squelch_count + 1; - dm->squelch_level = s->scan_squelch_level; -} - -void rtl_fm_scan_stop(void) -{ - struct controller_state *s = &controller; - struct demod_state *dm = &demod; - - s->scanning = 0; - - dm->squelch_hits = s->scan_squelch_count + 1; - dm->squelch_level = 0; -} - -void rtl_fm_scan_set_squelch_level(int level) -{ - struct controller_state *s = &controller; - - s->scan_squelch_level = level; -} - -void rtl_fm_scan_set_squelch_limit(int count) -{ - struct controller_state *s = &controller; - - s->scan_squelch_count = count; -} - -void rtl_fm_cleanup(void) -{ - //dongle_cleanup(&dongle); - demod_cleanup(&demod); - output_cleanup(&output); - controller_cleanup(&controller); - - rtlsdr_close(dongle.dev); -} - -// vim: tabstop=8:softtabstop=8:shiftwidth=8:noexpandtab diff --git a/binding/rtl_fm.h b/binding/rtl_fm.h deleted file mode 100644 index f5b2a86..0000000 --- a/binding/rtl_fm.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * rtl-sdr, turns your Realtek RTL2832 based DVB dongle into a SDR receiver - * Copyright (C) 2016, 2017 Konsulko Group - * - * 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/>. - */ - -#ifndef RTL_FM_H -#define RTL_FM_H - -#include <stdint.h> - -#define RTL_FM_DEFAULT_BUF_LENGTH (1 * 16384) -#define RTL_FM_MAXIMUM_OVERSAMPLE 16 -#define RTL_FM_MAXIMUM_BUF_LENGTH (RTL_FM_MAXIMUM_OVERSAMPLE * RTL_FM_DEFAULT_BUF_LENGTH) - -#ifdef __cplusplus -extern "C" { -#endif - -typedef void (*rtl_fm_output_fn_t)(int16_t *result, int result_len, void *data); - -int rtl_fm_init(uint32_t freq, - uint32_t sample_rate, - uint32_t resample_rate, - rtl_fm_output_fn_t output_fn, - void *output_fn_data); - -void rtl_fm_start(void); - -void rtl_fm_set_freq(uint32_t freq); - -void rtl_fm_set_freq_callback(void (*callback)(uint32_t, void *), - void *data); - -uint32_t rtl_fm_get_freq(void); - -void rtl_fm_stop(void); - -void rtl_fm_scan_start(int direction, - void (*callback)(uint32_t, void *), - void *data, - uint32_t step, - uint32_t min, - uint32_t max); - -void rtl_fm_scan_stop(void); - -void rtl_fm_scan_set_squelch_level(int level); - -void rtl_fm_scan_set_squelch_limit(int count); - -void rtl_fm_cleanup(void); - -#ifdef __cplusplus -} -#endif - -#endif /* RTL_FM_H */ diff --git a/package/config.xml b/package/config.xml index 158c5c7..9d59089 100644 --- a/package/config.xml +++ b/package/config.xml @@ -7,7 +7,7 @@ <author>Qt</author> <license>APL 2.0</license> <feature name="urn:AGL:widget:required-api"> - <param name="lib/libradio-binding.so" value="local" /> + <param name="radio" value="ws" /> </feature> <feature name="urn:AGL:widget:required-permission"> <param name="urn:AGL:permission::public:no-htdocs" value="required" /> @@ -1,3 +1,3 @@ TEMPLATE = subdirs -SUBDIRS = app binding package -package.depends += app binding +SUBDIRS = app package +package.depends += app |