diff options
Diffstat (limited to 'binding')
-rw-r--r-- | binding/CMakeLists.txt | 73 | ||||
-rw-r--r-- | binding/convenience/convenience.c | 309 | ||||
-rw-r--r-- | binding/convenience/convenience.h | 142 | ||||
-rw-r--r-- | binding/radio-binding.c | 752 | ||||
-rw-r--r-- | binding/radio_impl.h | 125 | ||||
-rw-r--r-- | binding/radio_impl_kingfisher.c | 526 | ||||
-rw-r--r-- | binding/radio_impl_kingfisher.h | 25 | ||||
-rw-r--r-- | binding/radio_impl_null.c | 281 | ||||
-rw-r--r-- | binding/radio_impl_null.h | 25 | ||||
-rw-r--r-- | binding/radio_impl_rtlsdr.c | 507 | ||||
-rw-r--r-- | binding/radio_impl_rtlsdr.h | 25 | ||||
-rw-r--r-- | binding/radio_impl_tef665x.c | 2405 | ||||
-rw-r--r-- | binding/radio_impl_tef665x.h | 23 | ||||
-rw-r--r-- | binding/radio_output.h | 31 | ||||
-rw-r--r-- | binding/radio_output_gstreamer.c | 231 | ||||
-rw-r--r-- | binding/rtl_fm.c | 1275 | ||||
-rw-r--r-- | binding/rtl_fm.h | 70 | ||||
-rw-r--r-- | binding/rtl_fm_helper.c | 243 | ||||
-rw-r--r-- | binding/tef665x.h | 397 |
19 files changed, 0 insertions, 7465 deletions
diff --git a/binding/CMakeLists.txt b/binding/CMakeLists.txt deleted file mode 100644 index 560dcaf..0000000 --- a/binding/CMakeLists.txt +++ /dev/null @@ -1,73 +0,0 @@ -########################################################################### -# Copyright 2015, 2016, 2017 IoT.bzh -# Copyright (C) 2018, 2019 Konsulko Group -# -# author: Fulup Ar Foll <fulup@iot.bzh> -# contrib: Romain Forlot <romain.forlot@iot.bzh> -# -# 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. -########################################################################### - -# Add target to project dependency list -PROJECT_TARGET_ADD(radio-binding) - - add_definitions(-DAFB_BINDING_VERSION=3) - - # Define project Targets - set(radio_SOURCES - radio-binding.c - radio_impl_kingfisher.c - radio_impl_null.c - radio_impl_rtlsdr.c - radio_impl_tef665x.c) - - PKG_CHECK_MODULES(SOUND REQUIRED gstreamer-1.0) - - add_library(${TARGET_NAME} MODULE ${radio_SOURCES}) - - # Binder exposes a unique public entry point - SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES - PREFIX "libafm-" - LABELS "BINDING" - LINK_FLAGS ${BINDINGS_LINK_FLAG} - OUTPUT_NAME ${TARGET_NAME} - ) - - # Library dependencies (include updates automatically) - TARGET_COMPILE_OPTIONS(${TARGET_NAME} PUBLIC ${SOUND_CFLAGS}) - TARGET_LINK_LIBRARIES(${TARGET_NAME} ${SOUND_LIBRARIES} ${link_libraries}) - -# Add helper program target -PROJECT_TARGET_ADD(rtl_fm_helper) - - # Define project targets - set(helper_SOURCES - ${TARGET_NAME}.c - radio_output_gstreamer.c - rtl_fm.c - convenience/convenience.c) - - PKG_CHECK_MODULES(helper_SOUND REQUIRED gstreamer-1.0) - PKG_CHECK_MODULES(helper_RTLSDR REQUIRED librtlsdr) - PKG_CHECK_MODULES(helper_LIBUSB REQUIRED libusb-1.0) - - add_executable(${TARGET_NAME} ${helper_SOURCES}) - - SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES - LABELS "EXECUTABLE" - OUTPUT_NAME ${TARGET_NAME} - ) - - TARGET_COMPILE_OPTIONS(${TARGET_NAME} PUBLIC ${helper_SOUND_CFLAGS}) - TARGET_LINK_LIBRARIES(${TARGET_NAME} - ${helper_RTLSDR_LIBRARIES} ${helper_LIBUSB_LIBRARIES} ${helper_SOUND_LIBRARIES} ${link_libraries} m) diff --git a/binding/convenience/convenience.c b/binding/convenience/convenience.c deleted file mode 100644 index ae1e24b..0000000 --- a/binding/convenience/convenience.c +++ /dev/null @@ -1,309 +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; - size_t len; - double suff = 1.0; - len = strlen(s); - last = s[len-1]; - s[len-1] = '\0'; - switch (last) { - case 'g': - case 'G': - suff *= 1e3; - /*@fallthrough@*/ - case 'm': - case 'M': - suff *= 1e3; - /*@fallthrough@*/ - 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; - size_t len; - double suff = 1.0; - len = strlen(s); - last = s[len-1]; - s[len-1] = '\0'; - switch (last) { - case 'h': - case 'H': - suff *= 60; - /*@fallthrough@*/ - case 'm': - case 'M': - suff *= 60; - /*@fallthrough@*/ - 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; - size_t 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; - ssize_t 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/radio-binding.c b/binding/radio-binding.c deleted file mode 100644 index 8b5559c..0000000 --- a/binding/radio-binding.c +++ /dev/null @@ -1,752 +0,0 @@ -/* - * Copyright (C) 2017, 2019 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 <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <stdint.h> -#include <stdbool.h> -#include <unistd.h> -#include <sys/types.h> -#include <json-c/json.h> -#include <afb/afb-binding.h> - -#include "radio_impl.h" -#include "radio_impl_null.h" -#include "radio_impl_rtlsdr.h" -#include "radio_impl_kingfisher.h" -#include "radio_impl_tef665x.h" - -static radio_impl_ops_t *radio_impl_ops; - -static afb_event_t freq_event; -static afb_event_t scan_event; -static afb_event_t status_event; -static afb_event_t rds_event; - -static bool playing; - -static const char *signalcomposer_events[] = { - "event.media.next", - "event.media.previous", - "event.media.mode", - NULL, -}; - -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)); -} - -static void rds_callback(void *rds_data) -{ - //rds_data is a json object - afb_event_push(rds_event, json_object_get(rds_data)); -} - -/* - * Binding verb handlers - */ - -/* - * @brief Get (and optionally set) frequency - * - * @param afb_req_t : an afb request structure - * - */ -static void frequency(afb_req_t request) -{ - json_object *ret_json; - const char *value = afb_req_value(request, "value"); - uint32_t frequency; - - if(value) { - char *p; - radio_band_t band; - uint32_t min_frequency; - uint32_t max_frequency; - uint32_t step; - - frequency = (uint32_t) strtoul(value, &p, 10); - if(frequency && *p == '\0') { - band = radio_impl_ops->get_band(); - min_frequency = radio_impl_ops->get_min_frequency(band); - max_frequency = radio_impl_ops->get_max_frequency(band); - step = radio_impl_ops->get_frequency_step(band); - if(frequency < min_frequency || - frequency > max_frequency || - (frequency - min_frequency) % step) { - afb_req_reply(request, NULL, "failed", "Invalid frequency"); - return; - } - radio_impl_ops->set_frequency(frequency); - } else { - afb_req_reply(request, NULL, "failed", "Invalid frequency"); - return; - } - } - ret_json = json_object_new_object(); - frequency = radio_impl_ops->get_frequency(); - json_object_object_add(ret_json, "frequency", json_object_new_int((int32_t) frequency)); - afb_req_reply(request, ret_json, NULL, NULL); -} - -/* - * @brief Get RDS information - * - * @param afb_req_t : an afb request structure - * - */ -static void rds(afb_req_t request) -{ - json_object *ret_json; - char * rds; - - if (radio_impl_ops->get_rds_info == NULL) { - afb_req_reply(request, NULL, "failed", "not supported"); - return; - } - - ret_json = json_object_new_object(); - rds = radio_impl_ops->get_rds_info(); - json_object_object_add(ret_json, "rds", json_object_new_string(rds?rds:"")); - free(rds); - - afb_req_reply(request, ret_json, NULL, NULL); -} - -/* @brief Get quality information - * - * @param afb_req_t : an afb request structure - * - */ -static void quality(afb_req_t request) -{ - json_object *ret_json; - station_quality_t *quality_data; - - if (radio_impl_ops->get_quality_info == NULL) { - afb_req_reply(request, NULL, "failed", "Not supported"); - return; - } - - quality_data = radio_impl_ops->get_quality_info(); - ret_json=json_object_new_object(); - if(quality_data->af_update) - { - json_object_object_add(ret_json, "af_update", json_object_new_int((int) quality_data->af_update)); - } - if(quality_data->time_stamp){ - json_object_object_add(ret_json, "timestamp", json_object_new_int((int) quality_data->time_stamp)); - } - if(quality_data->rssi) - { - json_object_object_add(ret_json, "rssi", json_object_new_int((int) quality_data->rssi)); - } - if(quality_data->usn) - { - json_object_object_add(ret_json, "usn", json_object_new_int((int) quality_data->usn)); - } - if(quality_data->bandw) - { - json_object_object_add(ret_json, "bandwidth", json_object_new_int((int) quality_data->bandw)); - } - afb_req_reply(request, ret_json, NULL, NULL); - return; -} - -/* @brief Check alternative frequency - * - * @param afb_req_t : an afb request structure - * - */ -static void alternative_frequency(afb_req_t request) -{ - json_object *ret_json; - uint32_t alternative; - const char *value; - - if (radio_impl_ops->set_alternative_frequency == NULL) { - afb_req_reply(request, NULL, "failed", "Not supported"); - return; - } - - value = afb_req_value(request, "value"); - if(value) { - char *p; - radio_band_t band; - uint32_t min_frequency; - uint32_t max_frequency; - uint32_t step; - - alternative = (uint32_t) strtoul(value, &p, 10); - if(alternative && *p == '\0') { - band = radio_impl_ops->get_band(); - min_frequency = radio_impl_ops->get_min_frequency(band); - max_frequency = radio_impl_ops->get_max_frequency(band); - step = radio_impl_ops->get_frequency_step(band); - if(alternative < min_frequency || - alternative > max_frequency || - (alternative - min_frequency) % step) { - afb_req_reply(request, NULL, "failed", "Invalid alternative frequency"); - return; - } - radio_impl_ops->set_alternative_frequency(alternative); - ret_json = json_object_new_object(); - json_object_object_add(ret_json, "alternative", json_object_new_int((int32_t) alternative)); - afb_req_reply(request, ret_json, NULL, NULL); - } else { - afb_req_reply(request, NULL, "failed", "Invalid alternative frequency"); - return; - } - } - else { - afb_req_reply(request, NULL, "failed", "Invalid alternative frequency"); - return; - } -} - -/* - * @brief Get (and optionally set) frequency band - * - * @param afb_req_t : an afb request structure - * - */ -static void band(afb_req_t 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_ops->set_band(band); - } else { - afb_req_reply(request, NULL, "failed", "Invalid band"); - return; - } - } - ret_json = json_object_new_object(); - band = radio_impl_ops->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_reply(request, ret_json, NULL, NULL); -} - -/* - * @brief Check if band is supported - * - * @param afb_req_t : an afb request structure - * - */ -static void band_supported(afb_req_t 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_reply(request, NULL, "failed", "Invalid band"); - return; - } - ret_json = json_object_new_object(); - json_object_object_add(ret_json, - "supported", - json_object_new_int(radio_impl_ops->band_supported(band))); - afb_req_reply(request, ret_json, NULL, NULL); -} - -/* - * @brief Get frequency range for a band - * - * @param afb_req_t : an afb request structure - * - */ -static void frequency_range(afb_req_t 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_reply(request, NULL, "failed", "Invalid band"); - return; - } - ret_json = json_object_new_object(); - min_frequency = radio_impl_ops->get_min_frequency(band); - max_frequency = radio_impl_ops->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_reply(request, ret_json, NULL, NULL); -} - -/* - * @brief Get frequency step size (Hz) for a band - * - * @param afb_req_t : an afb request structure - * - */ -static void frequency_step(afb_req_t 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_reply(request, NULL, "failed", "Invalid band"); - return; - } - ret_json = json_object_new_object(); - step = radio_impl_ops->get_frequency_step(band); - json_object_object_add(ret_json, "step", json_object_new_int((int32_t) step)); - afb_req_reply(request, ret_json, NULL, NULL); -} - -/* - * @brief Start radio playback - * - * @param afb_req_t : an afb request structure - * - */ -static void start(afb_req_t request) -{ - radio_impl_ops->set_output(NULL); - radio_impl_ops->start(); - playing = true; - afb_req_reply(request, NULL, NULL, NULL); - - json_object *jresp = json_object_new_object(); - json_object *value = json_object_new_string("playing"); - json_object_object_add(jresp, "value", value); - afb_event_push(status_event, json_object_get(jresp)); -} - -/* - * @brief Stop radio playback - * - * @param afb_req_t : an afb request structure - * - */ -static void stop(afb_req_t request) -{ - radio_impl_ops->stop(); - playing = false; - afb_req_reply(request, NULL, NULL, NULL); - - json_object *jresp = json_object_new_object(); - json_object *value = json_object_new_string("stopped"); - json_object_object_add(jresp, "value", value); - afb_event_push(status_event, json_object_get(jresp)); -} - -/* - * @brief Scan for a station in the specified direction - * - * @param afb_req_t : an afb request structure - * - */ -static void scan_start(afb_req_t request) -{ - 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_reply(request, NULL, "failed", "Invalid direction"); - return; - } - radio_impl_ops->scan_start(direction, scan_callback, NULL); - afb_req_reply(request, NULL, NULL, NULL); -} - -/* - * @brief Stop station scan - * - * @param afb_req_t : an afb request structure - * - */ -static void scan_stop(afb_req_t request) -{ - radio_impl_ops->scan_stop(); - afb_req_reply(request, NULL, NULL, NULL); -} - -/* - * @brief Get (and optionally set) stereo mode - * - * @param afb_req_t : an afb request structure - * - */ -static void stereo_mode(afb_req_t request) -{ - json_object *ret_json; - const char *value = afb_req_value(request, "value"); - int valid = 0; - radio_stereo_mode_t mode; - - 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_ops->set_stereo_mode(mode); - } else { - afb_req_reply(request, NULL, "failed", "Invalid mode"); - return; - } - } - ret_json = json_object_new_object(); - mode = radio_impl_ops->get_stereo_mode(); - - json_object_object_add(ret_json, "mode", json_object_new_string(mode == MONO ? "mono" : "stereo")); - afb_req_reply(request, ret_json, NULL, NULL); -} - -/* - * @brief Subscribe for an event - * - * @param afb_req_t : an afb request structure - * - */ -static void subscribe(afb_req_t 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 if(!strcasecmp(value, "status")) { - afb_req_subscribe(request, status_event); - } else if(!strcasecmp(value, "rds")) { - afb_req_subscribe(request, rds_event); - } - else { - afb_req_reply(request, NULL, "failed", "Invalid event"); - return; - } - } - afb_req_reply(request, NULL, NULL, NULL); -} - -/* - * @brief Unsubscribe for an event - * - * @param afb_req_t : an afb request structure - * - */ -static void unsubscribe(afb_req_t 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 if(!strcasecmp(value, "status")) { - afb_req_unsubscribe(request, status_event); - - } else if(!strcasecmp(value, "rds")) { - afb_req_unsubscribe(request, rds_event); - - } else { - afb_req_reply(request, NULL, "failed", "Invalid event"); - return; - } - } - afb_req_reply(request, NULL, NULL, NULL); -} - -static const afb_verb_t verbs[]= { - { .verb = "frequency", .session = AFB_SESSION_NONE, .callback = frequency, .info = "Get/Set frequency" }, - { .verb = "band", .session = AFB_SESSION_NONE, .callback = band, .info = "Get/Set band" }, - { .verb = "rds", .session = AFB_SESSION_NONE, .callback = rds, .info = "Get RDS information" }, - { .verb = "quality", .session = AFB_SESSION_NONE, .callback = quality, .info = "Get station quality information" }, - { .verb = "alternative_frequency", .session = AFB_SESSION_NONE, .callback = alternative_frequency, .info = "Check an alternative frequency" }, - { .verb = "band_supported", .session = AFB_SESSION_NONE, .callback = band_supported, .info = "Check band support" }, - { .verb = "frequency_range", .session = AFB_SESSION_NONE, .callback = frequency_range, .info = "Get frequency range" }, - { .verb = "frequency_step", .session = AFB_SESSION_NONE, .callback = frequency_step, .info = "Get frequency step" }, - { .verb = "start", .session = AFB_SESSION_NONE, .callback = start, .info = "Start radio playback" }, - { .verb = "stop", .session = AFB_SESSION_NONE, .callback = stop, .info = "Stop radio playback" }, - { .verb = "scan_start", .session = AFB_SESSION_NONE, .callback = scan_start, .info = "Start station scan" }, - { .verb = "scan_stop", .session = AFB_SESSION_NONE, .callback = scan_stop, .info = "Stop station scan" }, - { .verb = "stereo_mode", .session = AFB_SESSION_NONE, .callback = stereo_mode, .info = "Get/Set stereo_mode" }, - { .verb = "subscribe", .session = AFB_SESSION_NONE, .callback = subscribe, .info = "Subscribe for an event" }, - { .verb = "unsubscribe", .session = AFB_SESSION_NONE, .callback = unsubscribe, .info = "Unsubscribe for an event" }, - { } -}; - -static void onevent(afb_api_t api, const char *event, struct json_object *object) -{ - json_object *tmp = NULL; - const char *uid; - const char *value; - - json_object_object_get_ex(object, "uid", &tmp); - if (tmp == NULL) - return; - - uid = json_object_get_string(tmp); - if (strncmp(uid, "event.media.", 12)) - return; - - if (!playing || - (radio_impl_ops->get_corking_state && - radio_impl_ops->get_corking_state())) { - return; - } - - json_object_object_get_ex(object, "value", &tmp); - if (tmp == NULL) - return; - - value = json_object_get_string(tmp); - if (strncmp(value, "true", 4)) - return; - - if (!strcmp(uid, "event.media.next")) { - radio_impl_ops->scan_start(SCAN_FORWARD, scan_callback, NULL); - } else if (!strcmp(uid, "event.media.previous")) { - radio_impl_ops->scan_start(SCAN_BACKWARD, scan_callback, NULL); - } else if (!strcmp(uid, "event.media.mode")) { - // Do nothing ATM - } else { - AFB_WARNING("Unhandled signal-composer uid '%s'", uid); - } -} - -static int init(afb_api_t api) -{ - // Probe for radio backends - radio_impl_ops = &rtlsdr_impl_ops; - int rc = radio_impl_ops->probe(); - if(rc != 0) { - // Look for Kingfisher Si4689 - radio_impl_ops = &kf_impl_ops; - rc = radio_impl_ops->probe(); - } - if(rc != 0) { - radio_impl_ops = &tef665x_impl_ops; - rc = radio_impl_ops->probe(); - } - if (rc != 0) { - radio_impl_ops = &null_impl_ops; - rc = radio_impl_ops->probe(); - } - if (rc != 0) { - // We don't expect the null implementation to fail probe, but just in case... - AFB_API_ERROR(afbBindingV3root, "No radio device found, exiting"); - return rc; - } - // Try to initialize detected backend - rc = radio_impl_ops->init(); - if(rc < 0) { - AFB_API_ERROR(afbBindingV3root, - "%s initialization failed\n", - radio_impl_ops->name); - return rc; - } - AFB_API_NOTICE(afbBindingV3root, "%s found\n", radio_impl_ops->name); - radio_impl_ops->set_frequency_callback(freq_callback, NULL); - radio_impl_ops->set_frequency_callback(freq_callback, NULL); - if(radio_impl_ops->set_rds_callback) { - radio_impl_ops->set_rds_callback(rds_callback); - } - - rc = afb_daemon_require_api("signal-composer", 1); - if (rc) { - AFB_WARNING("unable to initialize signal-composer binding"); - } else { - const char **tmp = signalcomposer_events; - json_object *args = json_object_new_object(); - json_object *signals = json_object_new_array(); - - while (*tmp) { - json_object_array_add(signals, json_object_new_string(*tmp++)); - } - json_object_object_add(args, "signal", signals); - if(json_object_array_length(signals)) { - afb_api_call_sync(api, "signal-composer", "subscribe", - args, NULL, NULL, NULL); - } else { - json_object_put(args); - } - } - - // Initialize event structures - freq_event = afb_daemon_make_event("frequency"); - scan_event = afb_daemon_make_event("station_found"); - status_event = afb_daemon_make_event("status"); - rds_event = afb_daemon_make_event("rds"); - return 0; -} - -const afb_binding_t afbBindingExport = { - .info = "radio service", - .api = "radio", - .specification = "Radio API", - .verbs = verbs, - .onevent = onevent, - .init = init, -}; diff --git a/binding/radio_impl.h b/binding/radio_impl.h deleted file mode 100644 index 3e549ac..0000000 --- a/binding/radio_impl.h +++ /dev/null @@ -1,125 +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 void (*radio_rds_callback_t)(void *rds_data); - -typedef enum { - MONO = 0, - STEREO -} radio_stereo_mode_t; - -/* - * AF_update - * true if quality belongs to an alternative frequency - * - * time_stamp - * if time_stamp is zero, quality data won't be valid - * reliability depending on time stamp - * it takes some time after tuning to get valid quality data - * - * rssi - * (signed) - * level detector result(RF input level) - * - * usn - * FM ultrasonic noise detector (relative usn detector result) - * - * bandwidth - * IF bandwidth - */ -typedef struct -{ - bool af_update; - uint16_t time_stamp; - int16_t rssi; - uint16_t usn; - uint16_t bandw; -} station_quality_t; - -typedef struct { - char *name; - - int (*probe)(void); - - /* NOTE: init should return -1 if called before probe has been called and returned success */ - int (*init)(void); - - void (*set_output)(const char *output); - - uint32_t (*get_frequency)(void); - - void (*set_frequency)(uint32_t frequency); - - void (*set_frequency_callback)(radio_freq_callback_t callback, - void *data); - - void (*set_rds_callback)(radio_rds_callback_t callback); - - radio_band_t (*get_band)(void); - - void (*set_band)(radio_band_t band); - - int (*band_supported)(radio_band_t band); - - uint32_t (*get_min_frequency)(radio_band_t band); - - uint32_t (*get_max_frequency)(radio_band_t band); - - uint32_t (*get_frequency_step)(radio_band_t band); - - bool (*get_corking_state)(void); - - void (*start)(void); - - void (*stop)(void); - - void (*scan_start)(radio_scan_direction_t direction, - radio_scan_callback_t callback, - void *data); - - void (*scan_stop)(void); - - radio_stereo_mode_t (*get_stereo_mode)(void); - - void (*set_stereo_mode)(radio_stereo_mode_t mode); - - char * (*get_rds_info)(void); - - station_quality_t * (*get_quality_info)(void); - - void (*set_alternative_frequency)(uint32_t frequency); -} radio_impl_ops_t; - -#endif /* _RADIO_IMPL_H */ diff --git a/binding/radio_impl_kingfisher.c b/binding/radio_impl_kingfisher.c deleted file mode 100644 index e906140..0000000 --- a/binding/radio_impl_kingfisher.c +++ /dev/null @@ -1,526 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 <fcntl.h> -#include <sys/stat.h> -#include <json-c/json.h> -#include <gst/gst.h> - -#include <afb/afb-binding.h> - -#include "radio_impl.h" - -#define SI_NODE "/sys/firmware/devicetree/base/si468x@0/compatible" -#define SI_CTL "/usr/bin/si_ctl" -#define SI_CTL_CMDLINE_MAXLEN 128 -#define SI_CTL_OUTPUT_MAXLEN 128 - -#define GST_PIPELINE_LEN 256 - -// Flag to enable using GST_STATE_READY instead of GST_STATE_PAUSED to trigger -// Wireplumber policy mechanism. Hopefully temporary. -#define WIREPLUMBER_WORKAROUND - -// 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 = 76000000, .max = 95000000, .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 = 0; -static bool corking; -static bool present; -static bool initialized; -static uint32_t current_frequency; -static int scan_valid_snr_threshold = 128; -static int scan_valid_rssi_threshold = 128; -static bool scanning; - -// stream state -static GstElement *pipeline; -static bool running; - -static void (*freq_callback)(uint32_t, void*); -static void *freq_callback_data; - -static uint32_t kf_get_min_frequency(radio_band_t band); -static void kf_scan_stop(void); - -static gboolean handle_message(GstBus *bus, GstMessage *msg, __attribute__((unused)) void *ptr) -{ - GstState state; - - if (GST_MESSAGE_TYPE(msg) == GST_MESSAGE_REQUEST_STATE) { - gst_message_parse_request_state(msg, &state); - - if (state == GST_STATE_PAUSED) { - corking = true; - - // NOTE: Explicitly using PAUSED here, this case currently - // is separate from the general PAUSED/READY issue wrt - // Wireplumber policy. - gst_element_set_state(pipeline, GST_STATE_PAUSED); - } else if (state == GST_STATE_PLAYING) { - corking = false; - - gst_element_set_state(pipeline, GST_STATE_PLAYING); - } - } - - return TRUE; -} - -static void *gstreamer_loop_thread(void *ptr) -{ - g_main_loop_run(g_main_loop_new(NULL, FALSE)); - return NULL; -} - -static int kf_probe(void) -{ - struct stat statbuf; - - if(present) - return 0; - - // Check for Kingfisher SI468x devicetree node - if(stat(SI_NODE, &statbuf) != 0) - return -1; - - // Check for Cogent's si_ctl utility - if(stat(SI_CTL, &statbuf) != 0) - return -1; - - present = true; - return 0; -} - -static int kf_init(void) -{ - GKeyFile* conf_file; - bool conf_file_present = false; - char *value_str; - char cmd[SI_CTL_CMDLINE_MAXLEN]; - int rc; - char gst_pipeline_str[GST_PIPELINE_LEN]; - pthread_t thread_id; - - if(!present) - return -1; - - if(initialized) - return 0; - - // 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 = true; - - // 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; - } - } - } - } - - 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_valid_snr_threshold", - &error); - if(!error) { - AFB_API_INFO(afbBindingV3root, "Scan valid SNR level set to %d", n); - scan_valid_snr_threshold = n; - } - - error = NULL; - n = g_key_file_get_integer(conf_file, - "radio", - "scan_valid_rssi_threshold", - &error); - if(!error) { - AFB_API_INFO(afbBindingV3root, "Scan valid SNR level set to %d", n); - scan_valid_rssi_threshold = n; - } - - g_key_file_free(conf_file); - } - - AFB_API_INFO(afbBindingV3root, "Using FM Bandplan: %s", known_fm_band_plans[bandplan].name); - current_frequency = kf_get_min_frequency(BAND_FM); - snprintf(cmd, - sizeof(cmd), - "%s /dev/i2c-12 0x65 -b fm -p %s -t %d -u %d -c %d", - SI_CTL, - known_fm_band_plans[bandplan].name, - scan_valid_snr_threshold, - scan_valid_rssi_threshold, - current_frequency / 1000); - rc = system(cmd); - if(rc != 0) { - AFB_API_ERROR(afbBindingV3root, "%s failed, rc = %d", SI_CTL, rc); - return -1; - } - - // Initialize GStreamer - gst_init(NULL, NULL); - - // Use PipeWire output - rc = snprintf(gst_pipeline_str, - GST_PIPELINE_LEN, - "pipewiresrc stream-properties=\"p,node.target=alsa:pcm:radio:0:capture\" ! " - "audio/x-raw,format=F32LE,channels=2 ! " - "pipewiresink stream-properties=\"p,media.role=Multimedia\""); - if(rc >= GST_PIPELINE_LEN) { - AFB_API_ERROR(afbBindingV3root, "pipeline string too long"); - return -1; - } - pipeline = gst_parse_launch(gst_pipeline_str, NULL); - if(!pipeline) { - AFB_API_ERROR(afbBindingV3root, "pipeline construction failed!"); - return -1; - } - - // Start pipeline in paused state -#ifdef WIREPLUMBER_WORKAROUND - gst_element_set_state(pipeline, GST_STATE_READY); -#else - gst_element_set_state(pipeline, GST_STATE_PAUSED); -#endif - - gst_bus_add_watch(gst_element_get_bus(pipeline), (GstBusFunc) handle_message, NULL); - - rc = pthread_create(&thread_id, NULL, gstreamer_loop_thread, NULL); - if(rc != 0) - return rc; - - initialized = true; - return 0; -} - -static void kf_set_output(const char *output) -{ -} - -static uint32_t kf_get_frequency(void) -{ - return current_frequency; -} - -static void kf_set_frequency(uint32_t frequency) -{ - char cmd[SI_CTL_CMDLINE_MAXLEN]; - int rc; - - if(!initialized) - return; - - if(scanning) - return; - - if(frequency < known_fm_band_plans[bandplan].min || - frequency > known_fm_band_plans[bandplan].max) - return; - - kf_scan_stop(); - snprintf(cmd, sizeof(cmd), "%s /dev/i2c-12 0x65 -c %d", SI_CTL, frequency / 1000); - rc = system(cmd); - if(rc == 0) - current_frequency = frequency; - - if(freq_callback) - freq_callback(current_frequency, freq_callback_data); -} - -static void kf_set_frequency_callback(radio_freq_callback_t callback, - void *data) -{ - freq_callback = callback; - freq_callback_data = data; -} - -static char * kf_get_rds_info(void) { - char cmd[SI_CTL_CMDLINE_MAXLEN]; - char line[SI_CTL_OUTPUT_MAXLEN]; - char * rds = NULL; - FILE *fp; - - if (scanning) - goto done; - - snprintf(cmd, sizeof(cmd), "%s /dev/i2c-12 0x65 -m", SI_CTL); - fp = popen(cmd, "r"); - if(fp == NULL) { - fprintf(stderr, "Could not run: %s!\n", cmd); - goto done; - } - /* Look for "Name:" in output */ - while (fgets(line, sizeof(line), fp) != NULL) { - - char* nS = strstr(line, "Name:"); - char * end; - if (!nS) - continue; - - end = nS+strlen("Name:"); - /* remove the trailing '\n' */ - end[strlen(end)-1] = '\0'; - - rds = strdup(end); - break; - } - - /* Make sure si_ctl has finished */ - pclose(fp); - -done: - return rds; -} - -static radio_band_t kf_get_band(void) -{ - return BAND_FM; -} - -static void kf_set_band(radio_band_t band) -{ - // We only support FM, so do nothing -} - -static int kf_band_supported(radio_band_t band) -{ - if(band == BAND_FM) - return 1; - return 0; -} - -static uint32_t kf_get_min_frequency(radio_band_t band) -{ - return known_fm_band_plans[bandplan].min; -} - -static uint32_t kf_get_max_frequency(radio_band_t band) -{ - return known_fm_band_plans[bandplan].max; -} - -static uint32_t kf_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; -} - -static bool kf_get_corking_state(void) -{ - return corking; -} - -static void kf_start(void) -{ - if(!initialized) - return; - - if(!running || corking) { - // Start pipeline - gst_element_set_state(pipeline, GST_STATE_PLAYING); - running = true; - corking = false; - } -} - -static void kf_stop(void) -{ - GstEvent *event; - - if(initialized && running) { - // Stop pipeline - running = false; - -#ifdef WIREPLUMBER_WORKAROUND - // NOTE: Using NULL here instead of READY, as it seems to trigger - // some odd behavior in the pipeline; alsasrc does not seem to - // stop, and things get hung up on restart as there are a bunch - // of "old" samples that seemingly confuse pipewiresink. Going - // to NULL state seems to tear down things enough to avoid - // whatever happens. - gst_element_set_state(pipeline, GST_STATE_NULL); -#else - gst_element_set_state(pipeline, GST_STATE_PAUSED); -#endif - corking = false; - -#ifndef WIREPLUMBER_WORKAROUND - // Flush pipeline - // This seems required to avoid stutters on starts after a stop - event = gst_event_new_flush_start(); - gst_element_send_event(GST_ELEMENT(pipeline), event); - event = gst_event_new_flush_stop(TRUE); - gst_element_send_event(GST_ELEMENT(pipeline), event); -#endif - } -} - -static void kf_scan_start(radio_scan_direction_t direction, - radio_scan_callback_t callback, - void *data) -{ - int rc; - char cmd[SI_CTL_CMDLINE_MAXLEN]; - char line[SI_CTL_OUTPUT_MAXLEN]; - uint32_t new_frequency = 0; - FILE *fp; - - if(!initialized) - return; - - if(!running || scanning) - return; - - scanning = true; - snprintf(cmd, - SI_CTL_CMDLINE_MAXLEN, - "%s /dev/i2c-12 0x65 -l %s", - SI_CTL, direction == SCAN_FORWARD ? "up" : "down"); - fp = popen(cmd, "r"); - if(fp == NULL) { - AFB_API_ERROR(afbBindingV3root, "Could not run: %s!", cmd); - return; - } - // Look for "Frequency:" in output - while(fgets(line, SI_CTL_OUTPUT_MAXLEN, fp) != NULL) { - if(strncmp("Frequency:", line, 10) == 0) { - new_frequency = atoi(line + 10); - //AFB_API_DEBUG(afbBindingV3root, "%s: got new_frequency = %d", __FUNCTION__, new_frequency); - break; - } - } - - // Make sure si_ctl has finished - rc = pclose(fp); - if(rc != 0) { - // Make sure we reset to original frequency, the Si4689 seems - // to auto-mute sometimes on failed scans, this hopefully works - // around that. - new_frequency = 0; - } - - if(new_frequency) { - current_frequency = new_frequency * 1000; - - // Push up the new frequency - // This is more efficient than calling kf_set_frequency and calling - // out to si_ctl again. - if(freq_callback) - freq_callback(current_frequency, freq_callback_data); - } else { - // Assume no station found, go back to starting frequency - kf_set_frequency(current_frequency); - } - - // Push up scan state - if(callback) - callback(current_frequency, data); - - scanning = false; -} - -static void kf_scan_stop(void) -{ - // ATM, it's not straightforward to stop a scan since we're using the si_ctl utility... -} - -static radio_stereo_mode_t kf_get_stereo_mode(void) -{ - return STEREO; -} - -static void kf_set_stereo_mode(radio_stereo_mode_t mode) -{ - // We only support stereo, so do nothing -} - -radio_impl_ops_t kf_impl_ops = { - .name = "Kingfisher Si4689", - .probe = kf_probe, - .init = kf_init, - .set_output = kf_set_output, - .get_frequency = kf_get_frequency, - .set_frequency = kf_set_frequency, - .set_frequency_callback = kf_set_frequency_callback, - .get_band = kf_get_band, - .set_band = kf_set_band, - .band_supported = kf_band_supported, - .get_min_frequency = kf_get_min_frequency, - .get_max_frequency = kf_get_max_frequency, - .get_frequency_step = kf_get_frequency_step, - .get_corking_state = kf_get_corking_state, - .start = kf_start, - .stop = kf_stop, - .scan_start = kf_scan_start, - .scan_stop = kf_scan_stop, - .get_stereo_mode = kf_get_stereo_mode, - .set_stereo_mode = kf_set_stereo_mode, - .get_rds_info = kf_get_rds_info -}; diff --git a/binding/radio_impl_kingfisher.h b/binding/radio_impl_kingfisher.h deleted file mode 100644 index 5d4f064..0000000 --- a/binding/radio_impl_kingfisher.h +++ /dev/null @@ -1,25 +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_KINGFISHER_H -#define _RADIO_IMPL_KINGFISHER_H - -#include "radio_impl.h" - -extern radio_impl_ops_t kf_impl_ops; - -#endif /* _RADIO_IMPL_KINGFISHER_H */ - diff --git a/binding/radio_impl_null.c b/binding/radio_impl_null.c deleted file mode 100644 index b66c025..0000000 --- a/binding/radio_impl_null.c +++ /dev/null @@ -1,281 +0,0 @@ -/* - * Copyright (C) 2017,2018,2020 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. - */ - -/* - * NOTE: Some locking around frequency and scanning state may be - * required if this null implementation ever needs to be used - * beyond basic functionality testing. - */ - -#include <stdio.h> -#include <stdlib.h> -#include <stdint.h> -#include <stdbool.h> -#include <string.h> -#include <unistd.h> -#include <pthread.h> -#include <sys/stat.h> -#include <errno.h> -#include <glib.h> -#include <afb/afb-binding.h> - -#include "radio_impl.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 = 76000000, .max = 95000000, .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 initialized; -static bool active; -static bool scanning; -static uint32_t current_frequency; -static radio_freq_callback_t freq_callback; -static void *freq_callback_data; - -static uint32_t null_get_min_frequency(radio_band_t band); -static void null_set_frequency(uint32_t frequency); - -static int null_probe(void) -{ - present = true; - return 0; -} - -static int null_init(void) -{ - GKeyFile *conf_file; - char *value_str; - char *rootdir; - char *helper_path; - - if(!present) - return -1; - - if(initialized) - return 0; - - // 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) { - - // 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; - } - } - } - } - - // Start off with minimum bandplan frequency - current_frequency = null_get_min_frequency(BAND_FM); - - initialized = true; - null_set_frequency(current_frequency); - - return 0; -} - -static void null_set_output(const char *output) -{ -} - -static uint32_t null_get_frequency(void) -{ - return current_frequency; -} - -static void null_set_frequency(uint32_t frequency) -{ - if(frequency < known_fm_band_plans[bandplan].min || - frequency > known_fm_band_plans[bandplan].max) - return; - - current_frequency = frequency; - - if(freq_callback) - freq_callback(current_frequency, freq_callback_data); -} - -static void null_set_frequency_callback(radio_freq_callback_t callback, - void *data) -{ - freq_callback = callback; - freq_callback_data = data; -} - -static radio_band_t null_get_band(void) -{ - // We only support FM - return BAND_FM; -} - -static void null_set_band(radio_band_t band) -{ - // We only support FM, so do nothing -} - -static int null_band_supported(radio_band_t band) -{ - if(band == BAND_FM) - return 1; - return 0; -} - -static uint32_t null_get_min_frequency(radio_band_t band) -{ - return known_fm_band_plans[bandplan].min; -} - -static uint32_t null_get_max_frequency(radio_band_t band) -{ - return known_fm_band_plans[bandplan].max; -} - -static uint32_t null_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; -} - -static void null_start(void) -{ - if(!initialized) - return; - - if(active) - return; - - active = true; -} - -static void null_stop(void) -{ - if(!initialized) - return; - - if (!active) - return; - - active = false; -} - -static void null_scan_start(radio_scan_direction_t direction, - radio_scan_callback_t callback, - void *data) -{ - int frequency; - - if(!active || scanning) - return; - - scanning = true; - - // Just go to the next frequency step up or down - frequency = current_frequency; - if(direction == SCAN_FORWARD) { - frequency += known_fm_band_plans[bandplan].step; - } else { - frequency -= known_fm_band_plans[bandplan].step; - } - if(frequency < known_fm_band_plans[bandplan].min) - frequency = known_fm_band_plans[bandplan].max; - else if(frequency > known_fm_band_plans[bandplan].max) - frequency = known_fm_band_plans[bandplan].min; - - scanning = false; - null_set_frequency(frequency); - if(callback) - callback(frequency, data); -} - -static void null_scan_stop(void) -{ - scanning = false; -} - -static radio_stereo_mode_t null_get_stereo_mode(void) -{ - // We only support stereo - return STEREO; -} - -static void null_set_stereo_mode(radio_stereo_mode_t mode) -{ - // We only support stereo, so do nothing -} - -radio_impl_ops_t null_impl_ops = { - .name = "null/mock radio", - .probe = null_probe, - .init = null_init, - .set_output = null_set_output, - .get_frequency = null_get_frequency, - .set_frequency = null_set_frequency, - .set_frequency_callback = null_set_frequency_callback, - .get_band = null_get_band, - .set_band = null_set_band, - .band_supported = null_band_supported, - .get_min_frequency = null_get_min_frequency, - .get_max_frequency = null_get_max_frequency, - .get_frequency_step = null_get_frequency_step, - .start = null_start, - .stop = null_stop, - .scan_start = null_scan_start, - .scan_stop = null_scan_stop, - .get_stereo_mode = null_get_stereo_mode, - .set_stereo_mode = null_set_stereo_mode -}; diff --git a/binding/radio_impl_null.h b/binding/radio_impl_null.h deleted file mode 100644 index 16bd5e6..0000000 --- a/binding/radio_impl_null.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2020 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_NULL_H -#define _RADIO_IMPL_NULL_H - -#include "radio_impl.h" - -extern radio_impl_ops_t null_impl_ops; - -#endif /* _RADIO_IMPL_NULL_H */ - diff --git a/binding/radio_impl_rtlsdr.c b/binding/radio_impl_rtlsdr.c deleted file mode 100644 index 2087d10..0000000 --- a/binding/radio_impl_rtlsdr.c +++ /dev/null @@ -1,507 +0,0 @@ -/* - * Copyright (C) 2017,2018,2020 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 <unistd.h> -#include <pthread.h> -#include <sys/stat.h> -#include <errno.h> -#include <glib.h> -#include <afb/afb-binding.h> - -#include "radio_impl.h" - -#define HELPER_NAME "rtl_fm_helper" -#define HELPER_MAX PATH_MAX + 64 - -#define HELPER_CMD_MAXLEN 64 -#define HELPER_RSP_MAXLEN 128 - -// 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 = 76000000, .max = 95000000, .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 char *helper_output; -static pid_t helper_pid; -static int helper_in; -static int helper_out; -static pthread_mutex_t helper_mutex = PTHREAD_MUTEX_INITIALIZER; -static bool present; -static bool initialized; -static bool active; -static bool scanning; -static uint32_t current_frequency; -static radio_freq_callback_t freq_callback; -static void *freq_callback_data; - -static uint32_t rtlsdr_get_min_frequency(radio_band_t band); -static void rtlsdr_set_frequency(uint32_t frequency); -static void rtlsdr_scan_stop(void); - -/* - * Bi-directional popen implementation - * Based on one of the versions given in: - * - * https://stackoverflow.com/questions/12778672/killing-process-that-has-been-created-with-popen2 - */ -static pid_t popen2(char *command, int *in_fd, int *out_fd) -{ - int pin[2], pout[2]; - pid_t pid; - - if(out_fd != NULL) { - if(pipe(pin) != 0) - return -1; - } - if(in_fd != NULL) { - if (pipe(pout) != 0) { - if(out_fd != NULL) { - close(pin[0]); - close(pin[1]); - } - return -1; - } - } - - pid = fork(); - if(pid < 0) { - if(out_fd != NULL) { - close(pin[0]); - close(pin[1]); - } - if(in_fd != NULL) { - close(pout[0]); - close(pout[1]); - } - return pid; - } - if(pid == 0) { - if(out_fd != NULL) { - close(pin[1]); - dup2(pin[0], 0); - } - if(in_fd != NULL) { - close(pout[0]); - dup2(pout[1], 1); - } - execlp(command, command, NULL); - perror("Error:"); - exit(1); - } - if(in_fd != NULL) { - close(pout[1]); - *in_fd = pout[0]; - } - if(out_fd != NULL) { - close(pin[0]); - *out_fd = pin[1]; - } - return pid; -} - -static int rtlsdr_probe(void) -{ - char *rootdir; - char *helper_path; - - if(present) - return 0; - - rootdir = getenv("AFM_APP_INSTALL_DIR"); - if(!rootdir) - return -1; - - // Run helper to detect adapter - helper_path = malloc(HELPER_MAX); - if(!helper_path) - return -ENOMEM; - if(snprintf(helper_path, HELPER_MAX, "%s/bin/%s --detect", rootdir, HELPER_NAME) == HELPER_MAX) { - AFB_API_ERROR(afbBindingV3root, "Could not create command for \"%s --detect\"", HELPER_NAME); - return -EINVAL; - } - if(system(helper_path) != 0) { - free(helper_path); - return -1; - } - - present = true; - return 0; -} - -static int rtlsdr_start_helper(void) -{ - char *rootdir; - char *helper_path; - static bool helper_started = false; - - if(!present || initialized) - return -1; - - if(helper_started) - return 0; - - rootdir = getenv("AFM_APP_INSTALL_DIR"); - if(!rootdir) - return -1; - - if(helper_output) { - // Indicate desired output to helper - AFB_API_INFO(afbBindingV3root, "Setting RADIO_OUTPUT=%s", helper_output); - setenv("RADIO_OUTPUT", helper_output, 1); - } - - // Run helper - helper_path = malloc(HELPER_MAX); - if(!helper_path) - return -ENOMEM; - if(snprintf(helper_path, PATH_MAX, "%s/bin/%s", rootdir, HELPER_NAME) == PATH_MAX) { - AFB_API_ERROR(afbBindingV3root, "Could not create path to %s", HELPER_NAME); - return -EINVAL; - } - helper_pid = popen2(helper_path, &helper_out, &helper_in); - if(helper_pid < 0) { - AFB_API_ERROR(afbBindingV3root, "Could not run %s!", HELPER_NAME); - return -1; - } - AFB_API_DEBUG(afbBindingV3root, "%s started", HELPER_NAME); - helper_started = true; - free(helper_path); - - return 0; -} - -static int rtlsdr_init(void) -{ - GKeyFile *conf_file; - char *value_str; - int rc; - - if(!present) - return -1; - - if(initialized) - return 0; - - // 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) { - - // 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; - } - } - } - } - - // Start off with minimum bandplan frequency - current_frequency = rtlsdr_get_min_frequency(BAND_FM); - rc = rtlsdr_start_helper(); - if(rc != 0) { - return rc; - } - - initialized = true; - rtlsdr_set_frequency(current_frequency); - - return 0; -} - -static void rtlsdr_set_output(const char *output) -{ - // Save output for later use - free(helper_output); - helper_output = output ? strdup(output) : NULL; -} - -static uint32_t rtlsdr_get_frequency(void) -{ - return current_frequency; -} - -static void rtlsdr_set_frequency(uint32_t frequency) -{ - char cmd[HELPER_CMD_MAXLEN]; - char output[HELPER_RSP_MAXLEN]; - bool found = false; - ssize_t rc; - uint32_t n; - - if(!initialized) - return; - - if(frequency < known_fm_band_plans[bandplan].min || - frequency > known_fm_band_plans[bandplan].max) - return; - - if(scanning) - rtlsdr_scan_stop(); - - current_frequency = frequency; - snprintf(cmd, sizeof(cmd), "F=%u\n", frequency); - pthread_mutex_lock(&helper_mutex); - rc = write(helper_in, cmd, strlen(cmd)); - if(rc < 0) { - pthread_mutex_unlock(&helper_mutex); - return; - } - while(!found) { - rc = read(helper_out, output, sizeof(output)-1); - if(rc <= 0) - break; - output[rc] = '\0'; - if(output[0] == 'F') { - if(sscanf(output, "F=%u\n", &n) == 1) { - if(freq_callback) - freq_callback(n, freq_callback_data); - found = true; - } - } - } - pthread_mutex_unlock(&helper_mutex); -} - -static void rtlsdr_set_frequency_callback(radio_freq_callback_t callback, - void *data) -{ - freq_callback = callback; - freq_callback_data = data; -} - -static radio_band_t rtlsdr_get_band(void) -{ - // We only support FM - return BAND_FM; -} - -static void rtlsdr_set_band(radio_band_t band) -{ - // We only support FM, so do nothing -} - -static int rtlsdr_band_supported(radio_band_t band) -{ - if(band == BAND_FM) - return 1; - return 0; -} - -static uint32_t rtlsdr_get_min_frequency(radio_band_t band) -{ - return known_fm_band_plans[bandplan].min; -} - -static uint32_t rtlsdr_get_max_frequency(radio_band_t band) -{ - return known_fm_band_plans[bandplan].max; -} - -static uint32_t rtlsdr_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; -} - -static void rtlsdr_start(void) -{ - ssize_t rc; - char cmd[HELPER_CMD_MAXLEN]; - - if(!initialized) - return; - - if(active) - return; - - snprintf(cmd, sizeof(cmd), "START\n"); - pthread_mutex_lock(&helper_mutex); - rc = write(helper_in, cmd, strlen(cmd)); - pthread_mutex_unlock(&helper_mutex); - if (rc < 0) { - AFB_API_ERROR(afbBindingV3root, "Failed to ask \"%s\" to start", HELPER_NAME); - return; - } - active = true; -} - -static void rtlsdr_stop(void) -{ - ssize_t rc; - if(!initialized) - return; - - if (!active) - return; - - char cmd[HELPER_CMD_MAXLEN]; - - snprintf(cmd, sizeof(cmd), "STOP\n"); - pthread_mutex_lock(&helper_mutex); - rc = write(helper_in, cmd, strlen(cmd)); - pthread_mutex_unlock(&helper_mutex); - if (rc < 0) { - AFB_API_ERROR(afbBindingV3root, "Failed to ask \"%s\" to stop", HELPER_NAME); - return; - } - active = false; -} - -static void rtlsdr_scan_start(radio_scan_direction_t direction, - radio_scan_callback_t callback, - void *data) -{ - ssize_t rc; - char cmd[HELPER_CMD_MAXLEN]; - char output[HELPER_RSP_MAXLEN]; - bool found = false; - uint32_t n; - - if(!active || scanning) - return; - - scanning = true; - snprintf(cmd, - sizeof(cmd), - "S=%s\n", direction == SCAN_FORWARD ? "UP" : "DOWN"); - pthread_mutex_lock(&helper_mutex); - if(!scanning) { - pthread_mutex_unlock(&helper_mutex); - return; - } - rc = write(helper_in, cmd, strlen(cmd)); - pthread_mutex_unlock(&helper_mutex); - if(rc < 0) - return; - while(!found) { - pthread_mutex_lock(&helper_mutex); - if(!scanning) { - pthread_mutex_unlock(&helper_mutex); - break; - } - rc = read(helper_out, output, sizeof(output)-1); - pthread_mutex_unlock(&helper_mutex); - if(rc <= 0) - break; - - output[rc] = '\0'; - if(output[0] == 'F') { - if(sscanf(output, "F=%u\n", &n) == 1) { - current_frequency = n; - if(freq_callback) - freq_callback(n, freq_callback_data); - } - } - if(output[0] == 'S') { - if(sscanf(output, "S=%u\n", &n) == 1) { - if(callback) - callback(n, data); - found = true; - scanning = false; - } - } - } -} - -static void rtlsdr_scan_stop(void) -{ - char cmd[HELPER_CMD_MAXLEN]; - ssize_t rc; - - snprintf(cmd, sizeof(cmd), "S=STOP\n"); - pthread_mutex_lock(&helper_mutex); - scanning = false; - rc = write(helper_in, cmd, strlen(cmd)); - pthread_mutex_unlock(&helper_mutex); - if (rc < 0) - AFB_API_ERROR(afbBindingV3root, "Failed to ask \"%s\" to stop scan", HELPER_NAME); -} - -static radio_stereo_mode_t rtlsdr_get_stereo_mode(void) -{ - // We only support stereo - return STEREO; -} - -static void rtlsdr_set_stereo_mode(radio_stereo_mode_t mode) -{ - // We only support stereo, so do nothing -} - -radio_impl_ops_t rtlsdr_impl_ops = { - .name = "RTL-SDR USB adapter", - .probe = rtlsdr_probe, - .init = rtlsdr_init, - .set_output = rtlsdr_set_output, - .get_frequency = rtlsdr_get_frequency, - .set_frequency = rtlsdr_set_frequency, - .set_frequency_callback = rtlsdr_set_frequency_callback, - .get_band = rtlsdr_get_band, - .set_band = rtlsdr_set_band, - .band_supported = rtlsdr_band_supported, - .get_min_frequency = rtlsdr_get_min_frequency, - .get_max_frequency = rtlsdr_get_max_frequency, - .get_frequency_step = rtlsdr_get_frequency_step, - .start = rtlsdr_start, - .stop = rtlsdr_stop, - .scan_start = rtlsdr_scan_start, - .scan_stop = rtlsdr_scan_stop, - .get_stereo_mode = rtlsdr_get_stereo_mode, - .set_stereo_mode = rtlsdr_set_stereo_mode -}; diff --git a/binding/radio_impl_rtlsdr.h b/binding/radio_impl_rtlsdr.h deleted file mode 100644 index 6c83338..0000000 --- a/binding/radio_impl_rtlsdr.h +++ /dev/null @@ -1,25 +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_RTLSDR_H -#define _RADIO_IMPL_RTLSDR_H - -#include "radio_impl.h" - -extern radio_impl_ops_t rtlsdr_impl_ops; - -#endif /* _RADIO_IMPL_RTLSDR_H */ - diff --git a/binding/radio_impl_tef665x.c b/binding/radio_impl_tef665x.c deleted file mode 100644 index 3abcc00..0000000 --- a/binding/radio_impl_tef665x.c +++ /dev/null @@ -1,2405 +0,0 @@ -/* - * 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. - */ -/* - TODO: - at this point: - - complete the set stereo_mode verb. - - find a way to tell the service which i2c chanel is used. - - separate the functions of driver from the verbs by creating new c file. - - find a way of monitoring the quality of tuning and correct it time by time. - - use Interupt for getting RDS data -*/ -#include <stdio.h> -#include <stdlib.h> -#include <stdint.h> -#include <stdbool.h> -#include <string.h> -#include <unistd.h> -#include <glib.h> -#include <fcntl.h> -#include <sys/stat.h> -#include <sys/ioctl.h> -#include <linux/i2c-dev.h> -#include <linux/i2c.h> -#include <stdarg.h> -#include <error.h> -#include <gst/gst.h> -#include <time.h> -//#include <json-c/json.h> -//#include <gst/gst.h> - -#include <afb/afb-binding.h> - -#include <pthread.h> -#include "radio_impl.h" -#include "tef665x.h" - - -#define I2C_ADDRESS 0x64 -#define I2C_DEV "/dev/i2c-3" -#define VERSION "0.1" - -#define TEF665x_CMD_LEN_MAX 20 -#define SET_SUCCESS 1 -#define TEF665X_SPLIT_SIZE 24 - -#define TEF665x_REF_CLK 9216000 //reference clock frequency -#define TEF665x_IS_CRYSTAL_CLK 0 //crstal -#define TEF665x_IS_EXT_CLK 1 //external clock input - -#define High_16bto8b(a) ((uint8_t)((a) >> 8)) -#define Low_16bto8b(a) ((uint8_t)(a)) -#define Convert8bto16b(a) ((uint16_t)(((uint16_t)(*(a))) << 8 |((uint16_t)(*(a+1))))) - -#define GST_PIPELINE_LEN 256 - -const uint8_t tef665x_patch_cmdTab1[] = {3, 0x1c,0x00,0x00}; -const uint8_t tef665x_patch_cmdTab2[] = {3, 0x1c,0x00,0x74}; -const uint8_t tef665x_patch_cmdTab3[] = {3, 0x1c,0x00,0x75}; - - -typedef struct { - char *name; - uint32_t min; - uint32_t max; - uint32_t step; -} band_plan_t; - -typedef struct{ - radio_scan_callback_t callback; - radio_scan_direction_t direction; - void* data; -}scan_data_t; -typedef struct rds_data -{ - bool Text_Changed; - bool TrafficAnnouncement; - bool TrafficProgram; - bool Music_Speech; - - uint32_t Alternative_Freq[25]; - uint8_t Alternative_Freq_Counter; - uint8_t Num_AlterFreq; - - uint16_t PICode; - uint8_t DI_Seg; - uint8_t PTY_Code; - - uint8_t Year; - uint8_t Month; - uint8_t Day; - uint8_t Hour; - uint8_t Min; - - uint8_t PTYN_Size; - uint8_t raw_data[12]; - - char PS_Name[16]; - char RT[128]; - char PTYN[16]; -} rds_data_t; - -//thread for handling RDS and Mutex -pthread_t rds_thread; -rds_data_t RDS_Message; -pthread_mutex_t RDS_Mutex; - -station_quality_t quality; - -//Threads for handling Scan -pthread_t scan_thread; - -pthread_mutex_t scan_mutex; - -char _Temp[64]={0}; - -static band_plan_t known_fm_band_plans[5] = { - { .name = "US", .min = 87900000, .max = 107900000, .step = 200000 }, - { .name = "JP", .min = 76000000, .max = 95000000, .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 band_plan_t known_am_band_plans[1] = { - { .name = "W-ASIA", .min = 522000, .max = 1620000, .step = 9000 } -}; - -static unsigned int fm_bandplan = 2; -static unsigned int am_bandplan = 0; -static bool corking; -static bool present; -static bool initialized; -static bool scanning; - -// stream state -static GstElement *pipeline; -static bool running; - - -uint32_t AlterFreqOffset=0; - -static void (*freq_callback)(uint32_t, void*); -static void (*rds_callback) (void*); -static void *freq_callback_data; - -int tef665x_set_rds (uint32_t i2c_file_desc); -#define DEBUG 0 - -#if DEBUG == 1 -#define _debug(x, y) printf("function: %s, %s : %d\n", __FUNCTION__, #x, y) -#else -#define _debug(x, y) -#endif - -static uint32_t file_desc; - -static radio_band_t current_band; -static uint32_t current_am_frequency; -static uint32_t current_fm_frequency; - -static void tef665x_scan_stop (void); -static void tef665x_set_frequency (uint32_t); -static void tef665x_search_frequency (uint32_t); - -static uint32_t tef665x_get_min_frequency (radio_band_t); -static uint32_t tef665x_get_max_frequency (radio_band_t); -static uint32_t tef665x_get_frequency_step (radio_band_t); - -static station_quality_t *tef665x_get_quality_info (void); - -static gboolean handle_message(GstBus *bus, GstMessage *msg, __attribute__((unused)) void *ptr) -{ - GstState state; - - if (GST_MESSAGE_TYPE(msg) == GST_MESSAGE_REQUEST_STATE) { - - gst_message_parse_request_state(msg, &state); - - if (state == GST_STATE_PAUSED) - corking = true; - else if (state == GST_STATE_PLAYING) - corking = false; - - } - - return TRUE; -} - -static int tef665x_set_cmd(int i2c_file_desc, TEF665x_MODULE module, uint8_t cmd, int len, ...) -{ - int i, ret; - uint8_t buf[TEF665x_CMD_LEN_MAX]; - uint16_t temp; - va_list vArgs; - - va_start(vArgs, len); - - buf[0] = module; //module, FM/AM/APP - buf[1] = cmd; //cmd, 1,2,10,... - buf[2] = 0x01; //index, always 1 - - for(i = 3; i < len; i++) - { - temp = va_arg(vArgs,int); - - buf[i++] = High_16bto8b(temp); - buf[i] = Low_16bto8b(temp); - } - - va_end(vArgs); - - ret = write(i2c_file_desc, buf, len); - - temp = (ret == len) ? 1 : 0; - _debug("return value", temp); - return temp; -} - -static int tef665x_get_cmd(int i2c_file_desc, TEF665x_MODULE module, uint8_t cmd, uint8_t *receive, int len) -{ - uint8_t temp; - uint8_t buf[3]; - int ret; - - buf[0]= module; //module, FM/AM/APP - buf[1]= cmd; //cmd, 1,2,10,... - buf[2]= 1; //index, always 1 - - write(i2c_file_desc, buf, 3); - - ret = read(i2c_file_desc, receive, len); - temp = (ret == len) ? 1 : 0; - _debug("return value", temp); - if(temp==0) - AFB_ERROR("Error Number: %d: %s",errno,strerror(errno)); - return temp; -} - -/* -module 64 APPL -cmd 128 Get_Operation_Status | status -index -1 status - Device operation status - 0 = boot state; no command support - 1 = idle state - 2 = active state; radio standby - 3 = active state; FM - 4 = active state; AM -*/ -static int appl_get_operation_status(int i2c_file_desc ,uint8_t *status) -{ - uint8_t buf[2]; - int ret; - - ret = tef665x_get_cmd(i2c_file_desc, TEF665X_MODULE_APPL, - TEF665X_Cmd_Get_Operation_Status, - buf, sizeof(buf)); - - if(ret == SET_SUCCESS) - { - *status = Convert8bto16b(buf); - _debug("return value", 1); - return 1; - } - _debug("return value", 0); - return 0; -} - -static int get_operation_status(int i2c_file_desc, TEF665x_STATE *status) -{ - TEF665x_STATE data; - int ret; - if(SET_SUCCESS ==(ret = appl_get_operation_status(i2c_file_desc, &data))) - { - //printk( "appl_get_operation_status1 data= %d \n",data); - _debug("got status", ret); - switch(data) - { - case 0: - _debug("status: boot", ret); - *status = eDevTEF665x_Boot_state; - break; - case 1: - _debug("status: idle", ret); - *status = eDevTEF665x_Idle_state; - break; - default: - _debug("status: active", ret); - *status = eDevTEF665x_Active_state; - break; - } - } - return ret; -} - -static int tef665x_power_on(int i2c_file_desc) -{ - int ret; - TEF665x_STATE status; - usleep(5000); - if(SET_SUCCESS == (ret = get_operation_status(i2c_file_desc, &status))) //[ w 40 80 01 [ r 0000 ] - { - _debug("Powered ON", ret); - } - else - { - _debug("Powered ON FAILED!", ret); - } - - return ret; -} - -static int tef665x_writeTab(int i2c_file_desc,const uint8_t *tab) -{ - int ret; - ret = write(i2c_file_desc, tab + 1, tab[0]); - return (ret != tab[0]) ? 0 : 1; -} - -static int tef665x_patch_load(int i2c_file_desc, const uint8_t *bytes, uint16_t size) -{ - uint8_t buf[25]; //the size which we break the data into, is 24 bytes. - int ret, i; - - uint16_t num = size / 24; - uint16_t rem = size % 24; - - buf[0] = 0x1b; - - usleep(10000); - - for(i = 0; i < num; i++) - { - memcpy(buf + 1, bytes + (24 * i), 24); - - ret = write(i2c_file_desc, buf, 25); - - if(ret != 25) - { - _debug("FAILED, send patch error! in pack no", i); - return false; - } - usleep(50); - } - - memcpy(buf + 1, bytes + (num * 24), rem); - - ret = write(i2c_file_desc, buf, rem); - if(ret != rem) - { - _debug("FAILED, send patch error at the end!", 0); - return false; - } - usleep(50); - - _debug("return value", 1); - return true; -} - -static int tef665x_patch_init(int i2c_file_desc) -{ - int ret = 0; - ret = tef665x_writeTab(i2c_file_desc, tef665x_patch_cmdTab1); //[ w 1C 0000 ] - if(!ret) - { - _debug("1- tab1 load FAILED", ret); - return ret; - } - - ret = tef665x_writeTab(i2c_file_desc, tef665x_patch_cmdTab2); //[ w 1C 0074 ] - if(!ret) - { - _debug("2- tab2 load FAILED", ret); - return ret; - } - - ret = tef665x_patch_load(i2c_file_desc, pPatchBytes, patchSize); //table1 - if(!ret) - { - _debug("3- pPatchBytes load FAILED", ret); - return ret; - } - - ret = tef665x_writeTab(i2c_file_desc, tef665x_patch_cmdTab1); //[ w 1C 0000 ] - if(!ret) - { - _debug("4- tab1 load FAILED", ret); - return ret; - } - - ret = tef665x_writeTab(i2c_file_desc, tef665x_patch_cmdTab3); //[ w 1C 0075 ] - if(!ret) - { - _debug("5- tab3 load FAILED", ret); - return ret; - } - - ret = tef665x_patch_load(i2c_file_desc, pLutBytes, lutSize); //table2 - if(!ret) - { - _debug("6- pLutBytes load FAILED", ret); - return ret; - } - - ret = tef665x_writeTab(i2c_file_desc, tef665x_patch_cmdTab1); //[ w 1C 0000 ] - if(!ret) - { - _debug("7- tab1 load FAILED", ret); - return ret; - } - _debug("patch loaded", ret); - return ret; -} - -//Command start will bring the device into? idle state�: [ w 14 0001 ] -static int tef665x_start_cmd(int i2c_file_desc) -{ - - int ret; - unsigned char buf[3]; - - buf[0] = 0x14; - buf[1] = 0; - buf[2] = 1; - - ret = write(i2c_file_desc, buf, 3); - - if (ret != 3) - { - _debug("start cmd FAILED", 0); - return 0; - } - _debug("return true", 1); - return 1; -} - -static int tef665x_boot_state(int i2c_file_desc) -{ - int ret=0; - if(1 == tef665x_patch_init(i2c_file_desc)) - { - _debug("return true", 1); - } - else - { - _debug("return value", 0); - return 0; - } - - usleep(50000); - - if(1 == tef665x_start_cmd(i2c_file_desc)) - { - _debug("'start cmd'return true", 1); - } - else - { - _debug("return value", 0); - return 0; - } - - usleep(50000); - - return ret; -} - -/* -module 64 APPL -cmd 4 Set_ReferenceClock frequency - -index -1 frequency_high - [ 15:0 ] - MSB part of the reference clock frequency - [ 31:16 ] -2 frequency_low - [ 15:0 ] - LSB part of the reference clock frequency - [ 15:0 ] - frequency [*1 Hz] (default = 9216000) -3 type - [ 15:0 ] - clock type - 0 = crystal oscillator operation (default) - 1 = external clock input operation -*/ -static int tef665x_appl_set_referenceClock(uint32_t i2c_file_desc, uint16_t frequency_high, uint16_t frequency_low, uint16_t type) -{ - return tef665x_set_cmd(i2c_file_desc, TEF665X_MODULE_APPL, - TEF665X_Cmd_Set_ReferenceClock, - 9, - frequency_high, frequency_low, type); -} - -static int appl_set_referenceClock(uint32_t i2c_file_desc, uint32_t frequency, bool is_ext_clk) //0x3d 0x900 -{ - return tef665x_appl_set_referenceClock(i2c_file_desc,(uint16_t)(frequency >> 16), (uint16_t)frequency, is_ext_clk); -} - -/* -module 64 APPL -cmd 5 Activate mode - -index -1 mode - [ 15:0 ] - 1 = goto �active� state with operation mode of �radio standby� -*/ -static int tef665x_appl_activate(uint32_t i2c_file_desc ,uint16_t mode) -{ - return tef665x_set_cmd(i2c_file_desc, TEF665X_MODULE_APPL, - TEF665X_Cmd_Activate, - 5, - mode); -} - -static int appl_activate(uint32_t i2c_file_desc) -{ - return tef665x_appl_activate(i2c_file_desc, 1); -} -/* -module 48 AUDIO -cmd 22 set_dig_io signal, format, operation, samplerate - -index -1 signal -[ 15:0 ] - digital audio input / output - 32 = I²S digital audio IIS_SD_0 (input) - 33 = I²S digital audio IIS_SD_1 (output) -(2) mode - 0 = off (default) - 1 = input (only available for signal = 32) - 2 = output (only available for signal = 33) -(3) format - [ 15:0 ] - digital audio format select - 16 = I²S 16 bits (fIIS_BCK = 32 * samplerate) - 32 = I²S 32 bits (fIIS_BCK = 64 * samplerate) (default) - 272 = lsb aligned 16 bit (fIIS_BCK = 64 * samplerate) - 274 = lsb aligned 18 bit (fIIS_BCK = 64 * samplerate) - 276 = lsb aligned 20 bit (fIIS_BCK = 64 * samplerate) - 280 = lsb aligned 24 bit (fIIS_BCK = 64 * samplerate) -(4) operation - [ 15:0 ] - operation mode - 0 = slave mode; IIS_BCK and IIS_WS input defined by source (default) - 256 = master mode; IIS_BCK and IIS_WS output defined by device -(5) samplerate - [ 15:0 ] 3200 = 32.0 kHz - 4410 = 44.1 kHz (default) - 4800 = 48.0 kHz -*/ -static int tef665x_audio_set_dig_io(uint8_t i2c_file_desc, uint16_t signal, uint16_t mode, uint16_t format, uint16_t operation, uint16_t samplerate) -{ - int ret = tef665x_set_cmd(i2c_file_desc, TEF665X_MODULE_AUDIO, - TEF665X_Cmd_Set_Dig_IO, - 13, - signal, mode, format, operation, samplerate); - if(ret) - { - _debug("Digital In/Out is set ", signal); - } - else - { - _debug("FAILED, return", 0); - return 0; - } - return 1; -} - -/* -module 32 / 33 FM / AM -cmd 85 Set_Specials ana_out, dig_out - -index -1 signal - [ 15:0 ] - analog audio output - 128 = DAC L/R output -2 mode - [ 15:0 ] - output mode - 0 = off (power down) - 1 = output enabled (default) -*/ - -static int tef665x_audio_set_ana_out(uint32_t i2c_file_desc, uint16_t signal,uint16_t mode) -{ - int ret = tef665x_set_cmd(i2c_file_desc, TEF665X_MODULE_AUDIO, - TEF665X_Cmd_Set_Ana_Out, - 7, - signal, mode); - if(ret) - { - _debug("analog output is set to ", mode); - } - else - { - _debug("FAILED, return", 0); - return 0; - } - return 1; - -} - -/* -module 48 AUDIO -cmd 13 Set_Output_Source - -index -1 signal - [ 15:0 ] - audio output - 33 = I2S Digital audio - 128 = DAC L/R output (default) -2 source - [ 15:0 ] - source - 4 = analog radio - 32 = i2s digital audio input - 224 = audio processor (default) - 240 = sin wave generator -*/ -static int tef665x_set_output_src(uint32_t i2c_file_desc, uint8_t signal, uint8_t src) -{ - int ret = tef665x_set_cmd(i2c_file_desc, TEF665X_MODULE_AUDIO, - TEF665X_Cmd_Set_Output_Source, - 7, - signal, src); - if(ret) - { - _debug("Output is set ", signal); - } - else - { - _debug("FAILED, return", 0); - return 0; - } - return 1; -} - -static int tef665x_idle_state(int i2c_file_desc) -{ - TEF665x_STATE status; - - //mdelay(50); - - if(SET_SUCCESS == get_operation_status(i2c_file_desc, &status)) - { - _debug("got operation status", 1); - if(status != eDevTEF665x_Boot_state) - { - _debug("not in boot status", 1); - - if(SET_SUCCESS == appl_set_referenceClock(i2c_file_desc, TEF665x_REF_CLK, TEF665x_IS_CRYSTAL_CLK)) //TEF665x_IS_EXT_CLK - { - _debug("set the clock", TEF665x_REF_CLK); - if(SET_SUCCESS == appl_activate(i2c_file_desc))// APPL_Activate mode = 1.[ w 40 05 01 0001 ] - { - //usleep(100000); //Wait 100 ms - _debug("activate succeed", 1); - return 1; - } - else - { - _debug("activate FAILED", 1); - } - } - else - { - _debug("set the clock FAILED", TEF665x_REF_CLK); - } - - } - else - { - _debug("did not get operation status", 0); - } - - } - _debug("return value", 0); - return 0; -} - -static int tef665x_para_load(uint32_t i2c_file_desc) -{ - int i; - int r; - const uint8_t *p = init_para; - - for(i = 0; i < sizeof(init_para); i += (p[i]+1)) - { - if(SET_SUCCESS != (r = tef665x_writeTab(i2c_file_desc, p + i))) - { - break; - } - } - - //Initiate RDS - tef665x_set_rds(i2c_file_desc); - - _debug("return value", r); - return r; -} - -/* -module 32 / 33 FM / AM -cmd 1 Tune_To mode, frequency - -index -1 mode - [ 15:0 ] - tuning actions - 0 = no action (radio mode does not change as function of module band) - 1 = Preset Tune to new program with short mute time - 2 = Search Tune to new program and stay muted - FM 3 = AF-Update Tune to alternative frequency, store quality - and tune back with inaudible mute - 4 = Jump Tune to alternative frequency with short - inaudible mute - 5 = Check Tune to alternative frequency and stay - muted - AM 3 � 5 = reserved - 6 = reserved - 7 = End Release the mute of a Search or Check action - (frequency is not required and ignored) -2 frequency -[ 15:0 ] - tuning frequency - FM 6500 � 10800 65.00 � 108.00 MHz / 10 kHz step size - AM LW 144 � 288 144 � 288 kHz / 1 kHz step size - MW 522 � 1710 522 � 1710 kHz / 1 kHz step size - SW 2300 � 27000 2.3 � 27 MHz / 1 kHz step size -*/ -static int tef665x_radio_tune_to (uint32_t i2c_file_desc, bool fm, uint16_t mode,uint16_t frequency ) -{ - return tef665x_set_cmd(i2c_file_desc, fm ? TEF665X_MODULE_FM: TEF665X_MODULE_AM, - TEF665X_Cmd_Tune_To, - ( mode <= 5 ) ? 7 : 5, - mode, frequency); -} - -static int FM_tune_to(uint32_t i2c_file_desc, AR_TuningAction_t mode, uint16_t frequency) -{ - int ret = tef665x_radio_tune_to(i2c_file_desc, 1, (uint16_t)mode, frequency); - _debug("return value", ret); - return ret; -} - -static int AM_tune_to(uint32_t i2c_file_desc, AR_TuningAction_t mode,uint16_t frequency) -{ - int ret = tef665x_radio_tune_to(i2c_file_desc, 0, (uint16_t)mode, frequency); - _debug("return value", ret); - return ret; -} - -/* -module 48 AUDIO -cmd 11 Set_Mute mode - -index -1 mode - [ 15:0 ] - audio mute - 0 = mute disabled - 1 = mute active (default) -*/ -int tef665x_audio_set_mute(uint32_t i2c_file_desc, uint16_t mode) -{ - int ret = tef665x_set_cmd(i2c_file_desc, TEF665X_MODULE_AUDIO, - TEF665X_Cmd_Set_Mute, - 5, - mode); - if(ret) - { - _debug("mute state changed , mode", mode); - } - else - { - _debug("FAILED, return", 0); - return 0; - } - return 1; -} - -/* -module 48 AUDIO -cmd 10 Set_Volume volume - -index -1 volume - [ 15:0 ] (signed) - audio volume - -599 � +240 = -60 � +24 dB volume - 0 = 0 dB (default)f665x_patch_init function: "3"t,int16_t volume) -*/ -static int tef665x_audio_set_volume(uint32_t i2c_file_desc, uint16_t volume) -{ - return tef665x_set_cmd(i2c_file_desc, TEF665X_MODULE_AUDIO, - TEF665X_Cmd_Set_Volume, - 5, - volume*10); -} -/* -module 64 APPL -cmd 130 Get_Identification -index -1 device -2 hw_version -3 sw_version -*/ -int appl_get_identification(int i2c_file_desc) -{ - uint8_t buf[6]; - int ret; - - ret = tef665x_get_cmd(i2c_file_desc, TEF665X_MODULE_APPL, - TEF665X_Cmd_Get_Identification, - buf, sizeof(buf)); -// should be completed for further use -// extracting chip versions ... - if(ret == SET_SUCCESS) - { - for(int i = 0; i<6;i++) - printf("buf[%i] = %x\n", i, buf[i]); - return 1; - } - _debug("return value", 0); - return 0; -} - - -//mute=1, unmute=0 -int audio_set_mute(uint32_t i2c_file_desc, bool mute) -{ - return tef665x_audio_set_mute(i2c_file_desc, mute);//AUDIO_Set_Mute mode = 0 : disable mute -} - -//-60 � +24 dB volume -int audio_set_volume(uint32_t i2c_file_desc, int vol) -{ - return tef665x_audio_set_volume(i2c_file_desc, (uint16_t)vol); -} - -/* -module 64 APPL -cmd 1 Set_OperationMode mode - -index -1 mode - [ 15:0 ] - device operation mode - 0 = normal operation - 1 = radio standby mode (low-power mode without radio functionality) - (default) -*/ - -static int tef665x_audio_set_operationMode(uint32_t i2c_file_desc, uint16_t mode) -{ - _debug("normal: 0 standby: 1 requested", 1); - int ret = tef665x_set_cmd(i2c_file_desc, TEF665X_MODULE_APPL, - TEF665X_Cmd_Set_OperationMode, - 5, - mode); - if(ret) - { - _debug("was able to set the mode", ret); - } - else - { - _debug("FAILED, return", 0); - return 0; - } - return 1; -} - - - -//TRUE = ON; -//FALSE = OFF -static void radio_powerSwitch(uint32_t i2c_file_desc, bool OnOff) -{ - tef665x_audio_set_operationMode(i2c_file_desc, OnOff? 0 : 1);//standby mode = 1 -} - -static void radio_modeSwitch(uint32_t i2c_file_desc, bool mode_switch, AR_TuningAction_t mode, uint16_t frequency) -{ - - if(mode_switch) //FM - { - FM_tune_to(i2c_file_desc, mode, frequency); - } - else //AM - { - AM_tune_to(i2c_file_desc, mode, frequency); - } -} - -/* -module 32 FM -cmd 81 Set_RDS - -index -1 mode - [ 15:0 ] - RDS operation mode - 0 = OFF - 1 = decoder mode (default), output of RDS groupe data (Block A, B, C, D) - from get_rds_status, get_rds_data FM cmd 130/131 - -2 restart - [ 15:0 ] - RDS decoder restart - 0 = no control - 1 = manual restart, starlooking for new RDS data immidiately - 2 = automatic restart after tuning (default) - 3 = flush, empty RDS output buffer. - -3 interface - [ 15:0 ] - 0 = no pin interface. - 2 = data available status output; active low (GPIO feature 'DAVN') - 4 = legecy 2-wire demodulator data and clock output ('RDDA' and 'RDCL') -*/ -int tef665x_set_rds(uint32_t i2c_file_desc) -{ - return tef665x_set_cmd(i2c_file_desc, TEF665X_MODULE_FM, - TEF665X_Cmd_Set_RDS, - 9,//Total Bytes to be sent - TEF665X_Cmd_Set_RDS_mode, // default - TEF665X_Cmd_Set_RDS_autorestart, // restart after tune - 0x002 // no interface - ); -} - - -/* - * @brief Adding Alternative Frequencies to RDS Data Structure - * - * @param uint8_t* : raw data of alternative frequency (Group 0A of RDS) - * @param rds_data_t* : Pointer to RDS Data Structure - * @return void - */ -void Extract_Alt_Freqs(uint8_t* buf,rds_data_t *Rds_STU) -{ - for(int Buffer_Index=6;Buffer_Index<8;Buffer_Index++) - { - if(buf[Buffer_Index]>204){ - if(250>buf[Buffer_Index]&&buf[Buffer_Index]>224) - { - Rds_STU->Num_AlterFreq=buf[Buffer_Index]-224; - - if(Rds_STU->Alternative_Freq_Counter == Rds_STU->Num_AlterFreq) - { - Rds_STU->Alternative_Freq_Counter = 0; - } - AlterFreqOffset=87500000; - } - else if(buf[Buffer_Index]==205) - { - AFB_ERROR("Filler Code"); - } - else if(buf[Buffer_Index]==224) - { - AFB_ERROR("No AF Exists"); - } - else if(buf[Buffer_Index]==250) - { - AFB_ERROR("An LF/MF Frequency Follows"); - AlterFreqOffset=144000; - } - else if(buf[Buffer_Index]>250) - { - AFB_WARNING("Alternative Frequency Not Assigned"); - } - } - else if(buf[Buffer_Index]>0) - { - if(AlterFreqOffset == 87500000) - { - Rds_STU->Alternative_Freq[Rds_STU->Alternative_Freq_Counter]= - buf[Buffer_Index] * 100000 + AlterFreqOffset; - - Rds_STU->Alternative_Freq_Counter++; - - if(Rds_STU->Alternative_Freq_Counter == Rds_STU->Num_AlterFreq) - { - Rds_STU->Alternative_Freq_Counter = 0; - } - } - else if(AlterFreqOffset == 144000) - { - Rds_STU->Alternative_Freq[Rds_STU->Alternative_Freq_Counter]= - ((uint32_t)buf[Buffer_Index]) * 9000 + AlterFreqOffset; - - Rds_STU->Alternative_Freq_Counter++; - - if(Rds_STU->Alternative_Freq_Counter == Rds_STU->Num_AlterFreq) - { - Rds_STU->Alternative_Freq_Counter = 0; - } - } - else - { - AFB_WARNING("Alternative Frequency is not defined"); - } - } - else - { - AFB_ERROR("Alternative Frequency- Not to be used"); - } - } -} - -/* - * @brief Checking rds error code (determined by decoder) - * - * 0 : no error; block data was received with matching data and syndrome - * 1 : small error; possible 1 bit reception error detected; data is corrected - * 2 : large error; theoretical correctable error detected; data is corrected - * 3 : uncorrectable error; no data correction possible - * - * @param Errors : Error Code of blocks A,B,C and D of RDS - * @return void - */ -void Check_RDS_Error(uint8_t Errors[]) -{ - for (int i=0;i<4;i++){ - if(Errors[i]==1){ - AFB_WARNING("RDS Block %d Reception Error; small error; possible 1 bit reception error detected; data is corrected",i+1); - } - else if(Errors[i]==2){ - AFB_WARNING("RDS Block %d Reception Error; large error; theoretical correctable error detected; data is corrected",i+1); - } - else if(Errors[i]==3){ - AFB_ERROR("RDS Block %d Reception Error; uncorrectable error; no data correction possible",i+1); - } - } -} - -/* - * @brief Getting rds_data_t and Process its raw_data - * - * @param rds_data_t * : Pointer to latest RDS Data Structure - */ -void *Process_RDS_Words(void* rds_words){ - pthread_detach(pthread_self()); - - rds_data_t *Rds_STU = rds_words; - uint8_t *raw_data = Rds_STU->raw_data; - int8_t group_Ver = -1; - uint8_t GType0 = 0; - bool DI_Seg = 0; - bool M_S = 0; - bool TA = 0; - - //Parse 1st Section - bool DataAvailable = (raw_data[0] >> 7) & 1; - bool DataLoss = (raw_data[0] >> 6) & 1 == 1; - bool DataAvailType = (raw_data[0] >> 5) & 1 == 0; - bool GroupType = (raw_data[0] >> 4) & 1; - bool SyncStatus = (raw_data[0] >> 1) & 1; - - //Parse Last Section(Error Codes) - uint8_t Error_A = raw_data[10] >> 6; - uint8_t Error_B = raw_data[10] >> 4 & 3; - uint8_t Error_C = raw_data[10] >> 2 & 3; - uint8_t Error_D = raw_data[10] & 3; - uint8_t Errors[]={Error_A,Error_B,Error_C,Error_D}; - - //Inform user about Error Blocks Status Codes - Check_RDS_Error(Errors); - - if(Error_A==0){ - //Bytes 2 and 3 are inside Block A - //raw_data[2]and raw_data[3] Contains PI Code - Rds_STU->PICode=Convert8bto16b(&raw_data[2]); - } - else{ - AFB_ERROR("Error_A=%d",Error_A); - } - - bool GTypeVer=GType0; - uint16_t GType=raw_data[4]>>4; - //Bytes 4 and 5 are inside Block B - if(Error_B==0){ - GTypeVer=raw_data[4]>>3 & 1; - GType=raw_data[4]>>4; - Rds_STU->TrafficProgram=raw_data[4]>>2&1; - Rds_STU->PTY_Code= (raw_data[4] & 3) << 3 | raw_data[5] >> 5; - } - - //Position Of Character - uint8_t CharPos=0; - - //Extract Data based on Group Type values - switch (GType) - { - case 0: - { - if(Error_B==0) - { - CharPos = raw_data[5] & 3; - - Rds_STU->TrafficAnnouncement = raw_data[5] >> 4 & 1; - Rds_STU->Music_Speech = raw_data[5] >> 3 & 1; - Rds_STU->DI_Seg = (raw_data[5] >> 2 & 1) * (2 ^ (3 - CharPos)); - } - - if(Error_C==0) - { - //Group Type 0A - if (GType==0) - { - Extract_Alt_Freqs(raw_data,Rds_STU); - } - - //Group Type 0B - else - { - Rds_STU->PICode=Convert8bto16b(&raw_data[6]); - } - } - - if(Error_D == 0 && Error_B == 0) - { - if(raw_data[8] != 0x7f) - { - Rds_STU->PS_Name[CharPos*2] = raw_data[8]; - } - else - { - Rds_STU->PS_Name[CharPos*2] = (char)'\0'; - } - if(raw_data[9] != 0x7f) - { - Rds_STU->PS_Name[CharPos*2+1] = raw_data[9]; - } - else - { - Rds_STU->PS_Name[CharPos*2+1] = (char)'\0'; - } - } - } - break; - case 1: - { - //Group Type 1A - if(GTypeVer == 0) - { - if(Error_D == 0) - { - Rds_STU->Day = raw_data[8] >> 3; - Rds_STU->Hour = raw_data[8] >> 3; - Rds_STU->Min = ((raw_data[8] & 7) << 2) | (raw_data[9] >> 6) ; - } - } - } - break; - case 2: - { - //Group Type 2A: - if(GTypeVer == 0) - { - uint8_t Text_pos = raw_data[5] & 15; - - if(Error_B == 0 && Error_C == 0) - { - - if(raw_data[6] !=0x7f && raw_data[6] != '\n') - { - Rds_STU->RT[Text_pos*4] = raw_data[6]; - } - else{ - Rds_STU->RT[Text_pos*4] = (char)'\0'; - } - if(raw_data[7]!=0x7f&&raw_data[7]!='\n') - { - Rds_STU->RT[Text_pos*4+1] = raw_data[7]; - } - else - { - Rds_STU->RT[Text_pos*4+1] = (char)'\0'; - } - } - if(Error_B == 0 && Error_D == 0) - { - if(raw_data[8] != 0x7f && raw_data[8] != '\n') - { - Rds_STU->RT[Text_pos*4+2] = raw_data[8]; - } - else{ - Rds_STU->RT[Text_pos*4+2] = (char)'\0'; - } - if(raw_data[9] != 0x7f && raw_data[9] != '\n') - { - Rds_STU->RT[Text_pos*4+3] = raw_data[9]; - } - else - { - Rds_STU->RT[Text_pos*4+3] = (char)'\0'; - } - } - } - - //Group Type 2B: - else{ - if(Error_B==0 && Error_D==0) - { - //Clear All Radio Text if flag was changed - if(raw_data[5] >> 4 & 1 != Rds_STU->Text_Changed) - { - memcpy(Rds_STU->RT, _Temp , 64); - } - - uint8_t Text_pos = raw_data[5] & 15; - if(raw_data[8] != 0x7f && raw_data[8] != '\n') - { - - Rds_STU->RT[Text_pos*2] = raw_data[8]; - } - else{ - Rds_STU->RT[Text_pos*2] = (char)'\0'; - } - if(raw_data[9] != 0x7f && raw_data[9] != '\n') - { - Rds_STU->RT[Text_pos*2+1] = raw_data[9]; - } - else - { - Rds_STU->RT[Text_pos*2+1] = (char)'\0'; - } - } - } - } - break; - case 4: - { - //Group Type 4A - if(GTypeVer == 0) - { - if(Error_B == 0 && Error_C == 0 && Error_D == 0) - { - //Following caclulations are based on RDS Standard - uint32_t Modified_Julian_Day = ((raw_data[5] & 3) << 15) | (raw_data[6] << 7) | (raw_data[7]>>1); - int y2 = (int)((((double)Modified_Julian_Day)-15078.2)/365.25); - int m2 = (int)((((double)Modified_Julian_Day)-14956.1-((double)y2*365.25))/30.6001); - int d2 = (double)Modified_Julian_Day-14956-(int)(y2*365.25)-(int)(m2*30.6001); - int k = 0; - - if(m2 == 14 || m2 == 15) - { - k = 1; - } - - Rds_STU->Day = d2; - Rds_STU->Month = m2 - 1 + k * 12; - Rds_STU->Year = y2 + k; - - uint8_t UTCHour = ((raw_data[7] & 1) << 4) | (raw_data[8] >> 4); - uint8_t UTCMinute = ((raw_data[8] & 15) << 2) | (raw_data[9] >> 6); - - //Check Negative Offset - bool NegOff = raw_data[9] & 32; - uint8_t LocTimeOff = raw_data[9] & 31; - - if(!NegOff) - { - Rds_STU->Min = UTCMinute + LocTimeOff % 2; - while(UTCMinute > 60) - { - UTCHour++; - UTCMinute = UTCMinute - 60; - } - - Rds_STU->Hour = UTCHour + LocTimeOff / 2; - while(Rds_STU->Hour > 24){ - Rds_STU->Hour = Rds_STU->Hour - 24; - } - - - } - else{ - Rds_STU->Min = UTCMinute + LocTimeOff % 2; - while(UTCMinute < 0) - { - UTCHour--; - UTCMinute = UTCMinute + 60; - } - Rds_STU->Hour = UTCHour + LocTimeOff / 2; - while(Rds_STU->Hour<0) - { - Rds_STU->Hour = Rds_STU->Hour + 24; - - } - } - } - } - //Group Type 4B - else - { - AFB_WARNING("Groupe Type 4B are not supported yet"); - } - } - case 8: - { - AFB_WARNING("Groupe Type 8A and 8B are not supported yet"); - - } - case 10: - { - AFB_WARNING("Groupe Type 10A and 10B are not supported yet"); - /* - if(Error_B == 0){ - uint8_t pos = 0; - pos=(raw_data[5] & 1) * 4; - - if(Error_C == 0){ - Rds_STU->PTYN[pos] = raw_data[6]; - Rds_STU->PTYN[pos+1] = raw_data[7]; - Rds_STU->PTYN_Size = pos + 2; - } - if(Error_D == 0){ - Rds_STU->PTYN[pos+2] = raw_data[8]; - Rds_STU->PTYN[pos+3] = raw_data[9]; - Rds_STU->PTYN_Size = pos + 4; - } - } - /**/ - } - break; - default: - AFB_ERROR("Unsupported Group %d",GType); - break; - } - - if(!DataAvailable) - { - AFB_ERROR("RDS Data is not available"); - } - - if(DataLoss) - { - AFB_ERROR("previous data was not read, replaced by newer data"); - } - - if(GroupType == 0) - { - group_Ver = 0; - } - else - { - group_Ver = 1; - } - - if(!SyncStatus) - { - AFB_ERROR(" RDS decoder not synchronized; no RDS data found"); - } - - if(GroupType != GTypeVer) - { - AFB_ERROR("Version is not Correct?"); - } -} - -/* -module 32 FM -cmd 131 get RDS data - -index -1 status - [ 15:0 ] - FM RDS reception status. - [15] = dta availableflag - 0 = no data - 1 = data available - [14] = data loss flag - 0 = no data loss - 1 = previose data not read, replaced by newer data. - [13] = data available type - 0 = group data; continuos operation. - 1 = first PI data;data with PI code following decoder sync. - [12] = groupe type. - 0 = type A; A-B-C-D group (with PI code in the block A) - 1 = type B; A-B-C'-D group (with PI code in the block A and C') - [ 8:0 ] reserved - -2 A_Block - [ 15:0 ] = A block data - -3 B_Block - [ 15:0 ] = B block data - -4 C_Block - [ 15:0 ] = C block data - -5 D_Block - [ 15:0 ] = D block data - -6 dec error - [ 15:0 ] - error code determined by decoder - [ 15:14 ] = A block error - [ 13:12 ] = B block error - [ 11:10 ] = C block error - [ 9:8 ] = D block error - 0 = no error found - 1 = small error, correctable. data is corrected. - 2 = larg error, correctable. data is corrected. - 3 = uncorrectable error. - [ 7:0 ] = reserved. -*/ -/* - * @brief Get RDS Data fron Tef-665 - * - * Getting RDS Data From I2C and Calling a thread to process raw data - * - * @param i2c_file_desc : I2C File Descriptor - * @param Rds_STU : RDS Data Structure - * - */ -int tef665x_get_rds_data(uint32_t i2c_file_desc, rds_data_t *Rds_STU) -{ - - int ret; - uint8_t buf[12]; - - ret = tef665x_get_cmd(i2c_file_desc, TEF665X_MODULE_FM, - TEF665X_Cmd_Get_RDS_Data, - buf, sizeof(buf)); - - if(ret == 1) { - memcpy(Rds_STU->raw_data,buf,12); - pthread_t t0; - pthread_create(&t0, NULL,Process_RDS_Words ,(void *) (Rds_STU)); - } - return ret; -} - -void Clear_RDS_Data(rds_data_t *Rds_STU){ - - Rds_STU-> Text_Changed=0; - Rds_STU-> TrafficAnnouncement=0; - Rds_STU-> TrafficProgram=0; - Rds_STU-> Music_Speech=0; - - Rds_STU-> DI_Seg=0; - Rds_STU-> PTY_Code=0; - Rds_STU-> Num_AlterFreq=0; - Rds_STU->PTYN_Size=0; - - Rds_STU-> Day=0; - Rds_STU-> Month=0; - Rds_STU-> Year=0; - - Rds_STU-> Hour=0; - Rds_STU-> Min=0; - - /*memcpy(Rds_STU->Alternative_Freq,_Temp,25);/**/ - for(uint8_t i=0;i<25;i++){ - Rds_STU->Alternative_Freq[i]=0; - } - memcpy(Rds_STU-> PS_Name,_Temp,8); - Rds_STU-> PS_Name[0]='\0'; - memcpy(Rds_STU-> RT,_Temp,64); - Rds_STU-> RT[0]='\0'; - memcpy(Rds_STU-> PTYN,_Temp,8); - Rds_STU-> PTYN[0]='\0'; - - Rds_STU-> PICode=0; - Rds_STU->Alternative_Freq_Counter=0; - Rds_STU->PTYN_Size=0; -} - -//Check if RDS is available -int tef665x_get_rds_status(uint32_t i2c_file_desc, uint16_t *status) -{ - int ret = 0; - uint8_t buf[2]; - - ret = tef665x_get_cmd(i2c_file_desc, TEF665X_MODULE_FM, - TEF665X_Cmd_Get_RDS_Status, - buf, sizeof(buf)); - - if(ret == 1){ - status[0] =buf[0]; - status[1] =buf[1]; - } - - return ret; -} - -static int tef665x_wait_active(uint32_t i2c_file_desc) -{ - TEF665x_STATE status; - //usleep(50000); - if(SET_SUCCESS == appl_get_operation_status(i2c_file_desc, &status)) - { - AFB_INFO("got status", 1); - if((status != eDevTEF665x_Boot_state) && (status != eDevTEF665x_Idle_state)) - { - AFB_INFO("active status", 1); - - if(SET_SUCCESS == tef665x_para_load(i2c_file_desc)) - { - _debug("parameters loaded", 1); - } - else - { - _debug("parameters not loaded", 0); - return 0; - } - - if(current_band == BAND_FM){ - FM_tune_to(i2c_file_desc, eAR_TuningAction_Preset, current_fm_frequency / 10000);// tune to min - } else { - AM_tune_to(i2c_file_desc, eAR_TuningAction_Preset, current_am_frequency / 1000);// tune to min - } - - if(SET_SUCCESS == audio_set_mute(i2c_file_desc, 1))//unmute=0 - { - _debug("muted", 1); - } - else - { - _debug("not muted", 0); - return 0; - } - - // //if(SET_SUCCESS == audio_set_volume(i2c_file_desc, 35))//set to -10db - // { - // _debug("set vol to", 25); - // } - - // else - // { - // _debug("vol not set", 0); - // return 0; - // } - return 1; - } - } - - return 0; -} - -static void tef665x_chip_init(int i2c_file_desc) -{ - if(1 == tef665x_power_on(i2c_file_desc)) _debug("tef665x_power_on", 1); - usleep(50000); - if(1 == tef665x_boot_state(i2c_file_desc)) _debug("tef665x_boot_state", 1); - usleep(100000); - if(1 == tef665x_idle_state(i2c_file_desc)) _debug("tef665x_idle_state", 1); - usleep(200000); - if(1 == tef665x_wait_active(i2c_file_desc)) _debug("tef665x_wait_active", 1); - //if you want to use analog output comment below command, or pass 1 to it. - if(SET_SUCCESS != tef665x_audio_set_ana_out(i2c_file_desc, TEF665X_Cmd_Set_Output_signal_dac, 0)) - { - _debug("Set DAC to OFF failed", 0); - //return 0; - } - - if(SET_SUCCESS != tef665x_set_output_src(i2c_file_desc, TEF665X_Cmd_Set_Output_signal_i2s, - TEF665X_Cmd_Set_Output_source_aProcessor)) - { - _debug("Set output failed", 0); - //return 0; - } - //this is needed to use digital output - if(SET_SUCCESS != tef665x_audio_set_dig_io(i2c_file_desc, TEF665X_AUDIO_CMD_22_SIGNAL_i2s1, - TEF665X_AUDIO_CMD_22_MODE_voltage, - TEF665X_AUDIO_CMD_22_FORMAT_16, - TEF665X_AUDIO_CMD_22_OPERATION_slave, - TEF665X_AUDIO_CMD_22_SAMPLERATE_48K)) - { - _debug("Setup i2s failed", 0); - //return 0; - } - - -} - - -static int i2c_init(const char *i2c, int state, uint32_t *i2c_file_desc) -{ - int fd = 0, t; - - if(state == _open) - { - fd = open(i2c, O_RDWR); - - if(fd < 0) - { - _debug("could not open %s", i2c); - return fd; - } - - t = ioctl(fd, I2C_SLAVE, I2C_ADDRESS); - if (t < 0) - { - _debug("could not set up slave ", 0); - return t; - } - *i2c_file_desc = fd; - } - else - { - close(*i2c_file_desc); - } - - return 0; -} - -static void tef665x_start(void) -{ - int ret; - - if(!initialized) - return; - - _debug("file_desc ", file_desc); - - audio_set_mute(file_desc, 0); - - if(!running) { - - // Start pipeline - ret = gst_element_set_state(pipeline, GST_STATE_PLAYING); - _debug("gst_element_set_state to play", ret); - running = true; - } - } - -/* - * @brief Send_Rds_Result to rds subscribers - * - * @param rds_data_t : a rds message structure - * @return The JsonObject of rds info - */ -void *Send_Rds_Result(rds_data_t* RDS_Message){ - //Kill the thread when it was over - pthread_detach(pthread_self()); - - json_object *ret_json; - json_object *Alternative_Freqs; - - - ret_json = json_object_new_object(); - Alternative_Freqs=json_object_new_array(); - - - - for(uint8_t af=0 ; af<25 ; af++) - { - if(RDS_Message->Alternative_Freq[af]!=NULL&&RDS_Message->Alternative_Freq[af]!=0) - { - json_object_array_add(Alternative_Freqs,json_object_new_int(RDS_Message->Alternative_Freq[af])); - } - } - - //Prepare JSon Object - json_object_object_add(ret_json, "name" , json_object_new_string(RDS_Message->PS_Name)); - json_object_object_add(ret_json, "radiotext" , json_object_new_string(RDS_Message->RT)); - json_object_object_add(ret_json, "alternatives" , (Alternative_Freqs)); - json_object_object_add(ret_json, "minute" , json_object_new_int (RDS_Message->Min)); - json_object_object_add(ret_json, "hour" , json_object_new_int (RDS_Message->Hour)); - json_object_object_add(ret_json, "day" , json_object_new_int (RDS_Message->Day)); - json_object_object_add(ret_json, "month" , json_object_new_int (RDS_Message->Month)); - json_object_object_add(ret_json, "year" , json_object_new_int (RDS_Message->Year)); - json_object_object_add(ret_json, "pi" , json_object_new_int (RDS_Message->PICode)); - json_object_object_add(ret_json, "pty" , json_object_new_int (RDS_Message->PTY_Code)); - json_object_object_add(ret_json, "ta" , json_object_new_int (RDS_Message->TrafficAnnouncement)); - json_object_object_add(ret_json, "tp" , json_object_new_int (RDS_Message->TrafficProgram)); - json_object_object_add(ret_json, "ms" , json_object_new_int (RDS_Message->Music_Speech)); - - //Send JsonObject to rds Subscribers - if(rds_callback){ - rds_callback(ret_json); - } - - return ret_json; -} - -/* - * @brief Create an infinit Loop to get RDS Packets and Send them to subscribers - * - * RDS data will be available every 85 ms; - * Currently availability of RDS is checkes by tef665x_get_rds_status function - * - * @param rds_data_t : a rds message structure - * @return The JsonObject of latest rds info - */ -void *Get_RDS_Packets(rds_data_t *StuRDS){ - pthread_detach(pthread_self()); - uint32_t fd = 0; - - int ret = i2c_init(I2C_DEV, _open, &fd); - uint8_t status[2]; - - ret=tef665x_get_rds_status(fd, status); - - if(ret==1){ - if(status[0]>7){ - //RDS must update all the time, except the times we are scanning or changing frequency - //when scanning or changing frequncy, we unlock RDS_Mutex and it will end this thread - for (int ref_cnt=0; pthread_mutex_trylock(&RDS_Mutex) != 0;ref_cnt++){ - //Get New RDS Data - tef665x_get_rds_data(fd,StuRDS); - - //Send RDS Data after rexeiving 22 Packets - if(ref_cnt%22==0){ - pthread_t t0; - pthread_create(&t0, NULL,Send_Rds_Result ,(void *) (&RDS_Message)); - } - - //Wait for 85ms before reading available rds data - usleep(85000); - } - pthread_mutex_unlock (&RDS_Mutex); - } - - else{ - AFB_ERROR("RDS is Not Valid0"); - } - } - - else{ - AFB_ERROR("RDS is Not Valid1"); - } - i2c_init(I2C_DEV, _close, &fd); -} - -/* - * @brief Free Allocated Memory for Scan Thread and Unlock Scan Mutex - * - * @param scan_data : scan_data_t contains direction of search and callback - * for station_found event - */ -static void scan_cleanup_handler(void *scan_data) -{ - pthread_mutex_unlock(&scan_mutex); - free(scan_data); - scanning=false; -} - -/* - * @brief Create a loop to scan from current frequency to find a valid frequency - * - * If found a valid frequency, send station_found to subscribers and break the loop; - * If the direction was forward and reach the maximum frequency, Search Must continue - * from minimum frequency - * If the direction was backward and reach the minimum frequency, Search Must continue - * from maximum frequency - * If no valid frequency found, scan will stop at the begining point - * If stop_scan called, scan_mutex will be unlocked and thread must be stopped - * - * @param scan_data : scan_data_t contains direction of search and callback - * for station_found event - */ -void *scan_frequencies(scan_data_t* scan_data){ - pthread_cleanup_push(scan_cleanup_handler, (void *)scan_data); - - //Kill the thread when it was over - pthread_detach(pthread_self()); - - //Set Scan Flag - scanning=true; - - //"Unlock Mutex" Flag - bool unlck_mtx = false; - uint32_t new_freq = 0; - uint32_t init_freq = 0; - - init_freq = current_band == BAND_FM ? current_fm_frequency : current_am_frequency; - - //First Mute Current Frequency - tef665x_search_frequency(init_freq); - - //freq_step will be negative if direction was backward and positive if direction was forward - uint32_t freq_step = tef665x_get_frequency_step(current_band) * (scan_data->direction==SCAN_FORWARD?1:-1); - - //Continue loop until reaching the initial frequency - while(init_freq != new_freq) - { - //Check Status of scan_mutex - unlck_mtx = pthread_mutex_trylock(&scan_mutex)==0; - - //break the loop if scan_mutex was unlocked - if(unlck_mtx) - { - break; - } - - if(current_band==BAND_FM) - { - new_freq = current_fm_frequency + freq_step; - - //Searching Step is 100 KHz - //If frequency reached to initial point, the search must stop - while (((new_freq/10000)%10)!=0 && init_freq != new_freq){ - new_freq = new_freq+freq_step; - } - } - else - { - new_freq = current_am_frequency + freq_step; - } - - //Set Freq to min when it was more than Max Value - if(new_freq>tef665x_get_max_frequency(current_band)) - { - new_freq=tef665x_get_min_frequency(current_band); - } - - //Set Freq to max when it was less than Min Value - if(new_freq<tef665x_get_min_frequency(current_band)) - { - new_freq=tef665x_get_max_frequency(current_band); - } - - //Tune to new frequency - tef665x_search_frequency(new_freq); - - //wait 30 ms to make sure quality data is available - for(int i=0;i<40;i++) - { - usleep(1000); - - //Check scan_mutex lock for handling stop_scan - unlck_mtx=pthread_mutex_trylock(&scan_mutex)==0; - if(unlck_mtx) - { - break; - } - } - if(unlck_mtx) - { - break; - } - - //Get Quality of tuned frequeency - tef665x_get_quality_info();//Get_quality_status(); - - if((quality.rssi >260 /*&& ->.usn<100/**/) || quality.bandw>1200) - { - //Send frequency value - if(scan_data->callback) - { - scan_data->callback(new_freq,NULL); - } - - break; - } - usleep(100); - } - - //Calling last pthread_cleanup_push - pthread_cleanup_pop(1); -} - -/* - * @brief Get latest RDS Info and send rds jsonObject as response - * - * @return: cast rds_json(json_object) to (char *) and return result as response - */ -static char *tef665x_get_rds_info(void) -{ - //If Getting RDS Result wasn't already started, Start it now - if(pthread_mutex_trylock(&RDS_Mutex) == 0) - { - AFB_DEBUG("Create the thread."); - pthread_create(&rds_thread, NULL,Get_RDS_Packets ,(void *) (&RDS_Message)); - } - - //Send latest available rds data - json_object *rds_json=(json_object *)Send_Rds_Result(&RDS_Message); - - //Convert json_object to char* and send it as response - return (char *)json_object_to_json_string(rds_json); -} - -/* - * @brief Get latest quality Info and send quality parameters as response - * - * module 32/33 FM/AM - * cmd 129 Get_Quality_Data - * - * index - * 1 status - * [ 15:0 ] - * quality detector status - * [15] = AF_update flag - * 0 = continuous quality data with time stamp - * 1 = AF_Update sampled data - * [14:10] = reserved - * 0 = no data loss - * 1 = previose data not read, replaced by newer data. - * [9:0] = quality time stamp - * 0 = tuning is in progress, no quality data available - * 1 … 320 (* 0.1 ms) = 0.1 … 32 ms after tuning, - * quality data available, reliability depending on time stamp - * 1000 = > 32 ms after tuning - * quality data continuously updated - * - * 2 level - * [ 15:0 ] (signed) - * level detector result - * -200 … 1200 (0.1 * dBuV) = -20 … 120 dBuV RF input level - * actual range and accuracy is limited by noise and agc - * - * 3 usn - * [ 15:0 ] = noise detector - * FM ultrasonic noise detector - * 0 … 1000 (*0.1 %) = 0 … 100% relative usn detector result - * - * 4 wam - * [ 15:0 ] = radio frequency offset - * FM ‘wideband-AM’ multipath detector - * 0 … 1000 (*0.1 %) = 0 … 100% relative wam detector result - * - * 5 offset - * [ 15:0 ] (signed) = radio frequency offset - * -1200 … 1200 (*0.1 kHz) = -120 kHz … 120 kHz radio frequency error - * actual range and accuracy is limited by noise and bandwidth - * - * 6 bandwidth - * [ 15:0 ] = IF bandwidth - * FM 560 … 3110 [*0.1 kHz] = IF bandwidth 56 … 311 kHz; narrow … wide - * AM 30 … 80 [*0.1 kHz] = IF bandwidth 3 … 8 kHz; narrow … wide - * - * 7 modulation - * [ 15:0 ] = modulation detector - * FM 0 … 1000 [*0.1 %] = 0 … 100% modulation = 0 … 75 kHz FM dev. - * - * @return: cast station_quality_t pointer as response - * - */ - -static station_quality_t *tef665x_get_quality_info(void) -{ - uint32_t i2c_file_desc=0; - uint8_t data[14]; - - int ret = i2c_init(I2C_DEV, _open, &i2c_file_desc); - if(current_band==BAND_FM) - { - ret = tef665x_get_cmd(i2c_file_desc, TEF665X_MODULE_FM, - TEF665X_Cmd_Get_Quality_Data, - data, sizeof(data)); - } - else - { - ret = tef665x_get_cmd(i2c_file_desc, TEF665X_MODULE_AM, - TEF665X_Cmd_Get_Quality_Data, - data, sizeof(data)); - } - i2c_init(I2C_DEV, _close, &i2c_file_desc); - - quality.af_update = data[0]&0b10000000; - quality.time_stamp = ((data[0]&0b00000011)<<8 | data[1]); - quality.rssi = (data[2] << 8 | data[3] ); - quality.usn = (data[4] << 8 | data[5] ); - quality.bandw = (data[10]<< 8 | data[11]); - - return &quality; -} - -/* - * @brief Start Scan - * - * @param radio_scan_direction_t direction which is the scan direction and can be - * SCAN_FORWARD or SCAN_BACKWARD - * @param radio_scan_callback_t callback which is the callback for sending result of search to - * station_found ecent subscribers - * @return void - */ -static void tef665x_scan_start(radio_scan_direction_t direction, - radio_scan_callback_t callback, - void *data) -{ - //Stop RDS if enabled - pthread_mutex_unlock (&RDS_Mutex); - - //Stop current scan: - if(scanning) - { - tef665x_scan_stop(); - } - - scan_data_t *inputs; - - //Clean RDS Message since frequency will change - Clear_RDS_Data(&RDS_Message); - usleep(10000); - - AFB_DEBUG("check Mutex Condition"); - - //check if is there any activated search - if(pthread_mutex_trylock(&scan_mutex)==0&&!scanning) - { - AFB_DEBUG("Start Scanning..."); - - inputs=malloc(sizeof(*inputs)); - if(!inputs) - return -ENOMEM; - - inputs->direction= direction; - inputs->callback= callback; - inputs->data=data; - - pthread_create(&scan_thread, NULL,scan_frequencies ,(void *) inputs); - } -} - -/* - * @brief Stop Scan - * - * By unlocking scan_mutex, Scan thread will be stopped safely and update scanning flag - * - * @return void - */ -static void tef665x_scan_stop(void) -{ - pthread_mutex_unlock(&scan_mutex); - while(scanning) - { - usleep(100); - AFB_DEBUG(" Wait for unlocking scan Thread"); - } -} - -/* -module 32 / 33 FM / AM -cmd 133 Get_Signal_Status | status -index -1 status - [ 15:0 ] = Radio signal information - [15] = 0 : mono signal - [15] = 1 : FM stereo signal (stereo pilot detected) - - [14] = 0 : analog signal - [14] = 1 : digital signal (blend input activated by digital processor or control) - (TEF6659 only) -*/ -radio_stereo_mode_t tef665x_get_stereo_mode(void) -{ - uint32_t i2c_file_desc = 0; - int ret = i2c_init(I2C_DEV, _open, &i2c_file_desc); - uint8_t data[2]; - if(current_band==BAND_FM){ - ret = tef665x_get_cmd(i2c_file_desc, TEF665X_MODULE_FM, - TEF665X_Cmd_Get_Signal_Status, - data, sizeof(data)); - } - else{ - ret = tef665x_get_cmd(i2c_file_desc, TEF665X_MODULE_AM, - TEF665X_Cmd_Get_Signal_Status, - data, sizeof(data)); - } - i2c_init(I2C_DEV, _close, &i2c_file_desc); - return data[0]>>7 ==1 ? STEREO:MONO; -} - -static void tef665x_stop(void) -{ - int ret; - GstEvent *event; - audio_set_mute(file_desc, 1); - - if(initialized && running) { - // Stop pipeline - running = false; - ret = gst_element_set_state(pipeline, GST_STATE_PAUSED); - _debug("gst_element_set_state to pause", ret); - - // Flush pipeline - // This seems required to avoidstatic stutters on starts after a stop - event = gst_event_new_flush_start(); - gst_element_send_event(GST_ELEMENT(pipeline), event); - event = gst_event_new_flush_stop(TRUE); - gst_element_send_event(GST_ELEMENT(pipeline), event); - } -} - -static int tef665x_probe() -{ - int rc; - - if(present) - return 0; - - rc = i2c_init(I2C_DEV, _open, &file_desc); - if(rc < 0) { - AFB_NOTICE("tef665x not present"); - return -1; - } - _debug("file_desc= ", file_desc); - - rc = appl_get_identification(file_desc); - if(rc != 1){ - AFB_ERROR("no tef665x!"); - return -1; - } - - present = true; - return 0; -} - -static int tef665x_init() -{ - char gst_pipeline_str[GST_PIPELINE_LEN]; - int rc; - - if(!present) - return -1; - - if(initialized) - return 0; - - current_am_frequency = known_am_band_plans[am_bandplan].min; - current_fm_frequency = known_fm_band_plans[fm_bandplan].min; - - current_band = BAND_AM; - - radio_powerSwitch(file_desc, 1); - - tef665x_chip_init(file_desc); - - // Initialize GStreamer - gst_init(NULL, NULL); - - // Use PipeWire output - // This pipeline is working on imx6solo, the important thing, up to now, is that it gets xrun error every few seconds. - // I believe it's related to wireplumber on imx6. - rc = snprintf(gst_pipeline_str, - GST_PIPELINE_LEN, - "alsasrc device=hw:1,0 ! audioconvert ! audioresample ! audio/x-raw, rate=48000, channels=2 \ - ! pipewiresink stream-properties=\"p,media.role=Multimedia\""); - - if(rc >= GST_PIPELINE_LEN) { - AFB_ERROR("pipeline string too long"); - return -1; - } - printf("pipeline: , %s\n", gst_pipeline_str); - - pipeline = gst_parse_launch(gst_pipeline_str, NULL); - if(!pipeline) { - AFB_ERROR("pipeline construction failed!"); - return -1; - } - - // Start pipeline in paused state - rc = gst_element_set_state(pipeline, GST_STATE_PAUSED); - _debug("gst_element_set_state to pause (at the begining)", rc); - - rc = gst_bus_add_watch(gst_element_get_bus(pipeline), (GstBusFunc) handle_message, NULL); - _debug("gst_bus_add_watch rc", rc); - - //Initialize Mutex Lock for Scan and RDS - pthread_mutex_init(&scan_mutex, NULL); - pthread_mutex_init (&RDS_Mutex, NULL); - - initialized = true; - - tef665x_start(); - return 0; -} - -static void tef665x_set_frequency_callback(radio_freq_callback_t callback, - void *data) -{ - freq_callback = callback; - freq_callback_data = data; -} -static void tef665x_set_rds_callback(radio_rds_callback_t callback) -{ - rds_callback = callback; - -} -static void tef665x_set_output(const char *output) -{ -} - -static radio_band_t tef665x_get_band(void) -{ - _debug("band", current_band); - return current_band; -} - -static void tef665x_set_band(radio_band_t band) -{ - uint32_t fd = 0; - int ret = i2c_init(I2C_DEV, _open, &fd); - - _debug("i2c_init ret value", ret); - - if(band == BAND_FM){ - current_band = band; - FM_tune_to(fd, eAR_TuningAction_Preset, current_fm_frequency / 10000); - } else { - current_band = band; - AM_tune_to(fd, eAR_TuningAction_Preset, current_am_frequency / 1000); - } - - i2c_init(I2C_DEV, _close, &fd); - - _debug("band", current_band); -} - -static uint32_t tef665x_get_frequency(void) -{ - if(current_band == BAND_FM){ - return current_fm_frequency; - } else { - return current_am_frequency; - } -} - -static void tef665x_set_alternative_frequency(uint32_t frequency) -{ - uint32_t fd = 0; - int ret = i2c_init(I2C_DEV, _open, &fd); - - if(current_band == BAND_FM) - { - FM_tune_to(fd, eAR_TuningAction_AF_Update, frequency / 10000); - } - - i2c_init(I2C_DEV, _close, &fd); -} - -static void tef665x_set_frequency(uint32_t frequency) -{ - uint32_t fd = 0; - - if(!initialized) - return; - - if(scanning) - return; - - if(current_band == BAND_FM) { - if(frequency < known_fm_band_plans[fm_bandplan].min || - frequency > known_fm_band_plans[fm_bandplan].max ) { - _debug("invalid FM frequency", frequency); - return; - } - } else { - if(frequency < known_am_band_plans[am_bandplan].min || - frequency > known_am_band_plans[am_bandplan].max ) { - _debug("invalid AM frequency", frequency); - return; - } - } - - int ret = i2c_init(I2C_DEV, _open, &fd); - - if(current_band == BAND_FM){ - current_fm_frequency = frequency; - _debug("frequency set to FM :", frequency); - FM_tune_to(fd, eAR_TuningAction_Preset, frequency / 10000); - } else { - current_am_frequency = frequency; - _debug("frequency set to AM :", frequency); - AM_tune_to(fd, eAR_TuningAction_Preset, frequency / 1000); - } - i2c_init(I2C_DEV, _close, &fd); - - //Send Frequency data to subscribers - if(freq_callback) - { - freq_callback(frequency, freq_callback_data); - } - - //Start RDS if the band was FM - if(current_band==BAND_FM){ - //Unlock Mutex - pthread_mutex_unlock (&RDS_Mutex); - - //Clean RDS Message - Clear_RDS_Data(&RDS_Message); - - //Wait to make sure rds thread is finished - usleep(300000); - - //Restart RDS - tef665x_get_rds_info(); - } -} - -/* - * @brief Tune to a frequency in search mode - * - * Tune to new program and stay muted - * Sending new frequency to subscribers - * - * @param uint32_t which is the frequecy to be tuned - * @return void - */ -static void tef665x_search_frequency(uint32_t frequency) -{ - uint32_t fd = 0; - int ret = i2c_init(I2C_DEV, _open, &fd); - if(current_band == BAND_FM) - { - current_fm_frequency = frequency; - _debug("frequency set to FM :", frequency); - FM_tune_to(fd, eAR_TuningAction_Search, frequency / 10000); - - } - else - { - current_am_frequency = frequency; - _debug("frequency set to AM :", frequency); - AM_tune_to(fd, eAR_TuningAction_Search, frequency / 1000); - } - i2c_init(I2C_DEV, _close, &fd); - - //Send Frequency data to subscribers - if(freq_callback) - { - freq_callback(frequency, freq_callback_data); - } -} - -static int tef665x_band_supported(radio_band_t band) -{ - if(band == BAND_FM || band == BAND_AM) - return 1; - return 0; -} - -static uint32_t tef665x_get_min_frequency(radio_band_t band) -{ - if(band == BAND_FM) { - return known_fm_band_plans[fm_bandplan].min; - } else { - return known_am_band_plans[am_bandplan].min; - } -} - -static uint32_t tef665x_get_max_frequency(radio_band_t band) -{ - if(band == BAND_FM) { - return known_fm_band_plans[fm_bandplan].max; - } else { - return known_am_band_plans[am_bandplan].max; - } -} - -static uint32_t tef665x_get_frequency_step(radio_band_t band) -{ - uint32_t ret = 0; - - switch (band) { - case BAND_AM: - ret = known_am_band_plans[am_bandplan].step; - break; - case BAND_FM: - ret = known_fm_band_plans[fm_bandplan].step; - break; - default: - break; - } - return ret; -} - -radio_impl_ops_t tef665x_impl_ops = { - .name = "TEF665x", - .probe = tef665x_probe, - .init = tef665x_init, - .start = tef665x_start, - .stop = tef665x_stop, - .set_output = tef665x_set_output, - .get_frequency = tef665x_get_frequency, - .set_frequency = tef665x_set_frequency, - .set_frequency_callback = tef665x_set_frequency_callback, - .set_rds_callback=tef665x_set_rds_callback, - .get_band = tef665x_get_band, - .set_band = tef665x_set_band, - .band_supported = tef665x_band_supported, - .get_min_frequency = tef665x_get_min_frequency, - .get_max_frequency = tef665x_get_max_frequency, - .get_frequency_step = tef665x_get_frequency_step, - .scan_start = tef665x_scan_start, - .scan_stop = tef665x_scan_stop, - .get_stereo_mode = tef665x_get_stereo_mode, - //.set_stereo_mode = tef665x_set_stereo_mode,*/ - .get_rds_info = tef665x_get_rds_info, - .get_quality_info = tef665x_get_quality_info, - .set_alternative_frequency = tef665x_set_alternative_frequency -}; diff --git a/binding/radio_impl_tef665x.h b/binding/radio_impl_tef665x.h deleted file mode 100644 index a95fd99..0000000 --- a/binding/radio_impl_tef665x.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * 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_TEF665X_H -#define _RADIO_IMPL_TEF665X_H - -#include "radio_impl.h" - -extern radio_impl_ops_t tef665x_impl_ops; - - -#endif /* _RADIO_IMPL_TEF665X_H */ - diff --git a/binding/radio_output.h b/binding/radio_output.h deleted file mode 100644 index bfa13cd..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); - -ssize_t radio_output_write(void *buf, int len); - -#endif /* _RADIO_OUTPUT_H */ - diff --git a/binding/radio_output_gstreamer.c b/binding/radio_output_gstreamer.c deleted file mode 100644 index e098d2d..0000000 --- a/binding/radio_output_gstreamer.c +++ /dev/null @@ -1,231 +0,0 @@ -/* - * Copyright (C) 2018, 2019 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 <stdbool.h> -#include <string.h> -#include <errno.h> -#include <gst/gst.h> - -#include "radio_output.h" -#include "rtl_fm.h" - -// Flag to enable using GST_STATE_READY instead of GST_STATE_PAUSED to trigger -// Wireplumber policy mechanism. Hopefully temporary. -#define WIREPLUMBER_WORKAROUND - -// Output buffer -static unsigned int extra; -static int16_t extra_buf[1]; -static unsigned char *output_buf; - -// GStreamer state -static GstElement *pipeline, *appsrc; -static bool running; - -int radio_output_open() -{ - unsigned int rate = 24000; - GstElement *queue, *convert, *sink, *resample; - char *p; - - if(pipeline) - return 0; - - // Initialize GStreamer -#ifdef DEBUG - unsigned int argc = 2; - char **argv = malloc(2 * sizeof(char*)); - argv[0] = strdup("test"); - argv[1] = strdup("--gst-debug-level=5"); - gst_init(&argc, &argv); -#else - gst_init(NULL, NULL); -#endif - - // Setup pipeline - // NOTE: With our use of the simple buffer pushing mode, there seems to - // be no need for a mainloop, so currently not instantiating one. - pipeline = gst_pipeline_new("pipeline"); - appsrc = gst_element_factory_make("appsrc", "source"); - queue = gst_element_factory_make("queue", "queue"); - convert = gst_element_factory_make("audioconvert", "convert"); - resample = gst_element_factory_make("audioresample", "resample"); - sink = gst_element_factory_make("pipewiresink", "sink"); - if(!(pipeline && appsrc && queue && convert && resample && sink)) { - fprintf(stderr, "pipeline element construction failed!\n"); - } - g_object_set(G_OBJECT(appsrc), "caps", - gst_caps_new_simple("audio/x-raw", - "format", G_TYPE_STRING, "S16LE", - "rate", G_TYPE_INT, rate, - "channels", G_TYPE_INT, 2, - "layout", G_TYPE_STRING, "interleaved", - "channel-mask", G_TYPE_UINT64, 3, - NULL), NULL); - gst_util_set_object_arg(sink, "stream-properties", "p,media.role=Multimedia"); - - if((p = getenv("RADIO_OUTPUT"))) { - fprintf(stderr, "Using output device %s\n", p); - g_object_set(sink, "device", p, NULL); - } - gst_bin_add_many(GST_BIN(pipeline), appsrc, queue, convert, resample, sink, NULL); - gst_element_link_many(appsrc, queue, convert, resample, sink, NULL); - //gst_bin_add_many(GST_BIN(pipeline), appsrc, convert, resample, sink, NULL); - //gst_element_link_many(appsrc, convert, resample, sink, NULL); - - // Set up appsrc - // NOTE: Radio seems like it matches the use case the "is-live" property - // is for, but setting it seems to require a lot more work with - // respect to latency settings to make the pipeline work smoothly. - // For now, leave it unset since the result seems to work - // reasonably well. - g_object_set(G_OBJECT(appsrc), - "stream-type", 0, - "format", GST_FORMAT_TIME, - NULL); - - // Start pipeline in paused state -#ifdef WIREPLUMBER_WORKAROUND - gst_element_set_state(pipeline, GST_STATE_READY); -#else - gst_element_set_state(pipeline, GST_STATE_PAUSED); -#endif - - // Set up output buffer - extra = 0; - output_buf = malloc(sizeof(unsigned char) * RTL_FM_MAXIMUM_BUF_LENGTH); - - return 0; -} - -int radio_output_start(void) -{ - int rc = 0; - - if(!pipeline) { - rc = radio_output_open(); - if(rc) - return rc; - } - - // Start pipeline - running = true; - gst_element_set_state(pipeline, GST_STATE_PLAYING); - - return rc; -} - -void radio_output_stop(void) -{ - GstEvent *event; - - if(pipeline && running) { - // Stop pipeline - running = false; -#ifdef WIREPLUMBER_WORKAROUND - gst_element_set_state(pipeline, GST_STATE_READY); -#else - gst_element_set_state(pipeline, GST_STATE_PAUSED); -#endif - - // Flush pipeline - // This seems required to avoid stutters on starts after a stop - event = gst_event_new_flush_start(); - gst_element_send_event(GST_ELEMENT(pipeline), event); - event = gst_event_new_flush_stop(TRUE); - gst_element_send_event(GST_ELEMENT(pipeline), event); - } -} - -void radio_output_suspend(int state) -{ - // Placeholder -} - -void radio_output_close(void) -{ - radio_output_stop(); - - if(pipeline) { - // Tear down pipeline - gst_element_set_state(pipeline, GST_STATE_NULL); - gst_object_unref(GST_OBJECT(pipeline)); - pipeline = NULL; - running = false; - } - - free(output_buf); - output_buf = NULL; -} - -ssize_t radio_output_write(void *buf, int len) -{ - ssize_t rc = -EINVAL; - size_t n = len; - int samples = len / 2; - unsigned char *p; - GstBuffer *buffer; - GstFlowReturn ret; - - if(!(pipeline && buf)) { - return rc; - } - - // Don't bother pushing samples if output hasn't started - if(!running) - return 0; - - /* - * Handle the rtl_fm code giving us an odd number of samples. - * 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; - } - - // Push buffer into pipeline - buffer = gst_buffer_new_allocate(NULL, n, NULL); - gst_buffer_fill(buffer, 0, p, n); - g_signal_emit_by_name(appsrc, "push-buffer", buffer, &ret); - gst_buffer_unref(buffer); - rc = n; - - return rc; -} diff --git a/binding/rtl_fm.c b/binding/rtl_fm.c deleted file mode 100644 index cfbd487..0000000 --- a/binding/rtl_fm.c +++ /dev/null @@ -1,1275 +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; - uint8_t tmp; - - for (i=0; i<len; i+=8) { - /* uint8_t negation = 255 - x */ - tmp = (uint8_t)(255 - buf[i+3]); - buf[i+3] = buf[i+2]; - buf[i+2] = tmp; - - buf[i+4] = (uint8_t)(255 - buf[i+4]); - buf[i+5] = (uint8_t)(255 - buf[i+5]); - - tmp = (uint8_t)(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] = (int16_t)d->now_r; // * d->output_scale; - d->lowpassed[i2+1] = (int16_t)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] = (int16_t) ((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] = (int16_t) ((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. */ -{ - int16_t temp; - int sum; - int d; - 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] = (int16_t) (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 */ - const int ret = 1<<13; - return (cj > 0) ? ret : -ret; - } - - 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) ((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)((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)((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 = (int)(sum / fm->result_len); - avg = (avg + fm->dc_avg * 9) / 10; - for (i=0; i < fm->result_len; i++) { - fm->result[i] = (int16_t)(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; - double 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)(buf2[j] + (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] = (int16_t) (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); -} - -int sanity_checks(void) -{ - int r = 1; - if (controller.freq_len == 0) { - fprintf(stderr, "Please specify a frequency.\n"); - r = 0; - } - - if (controller.freq_len >= FREQUENCIES_LIMIT) { - fprintf(stderr, "Too many channels, maximum %i.\n", FREQUENCIES_LIMIT); - r = 0; - } - - if (controller.freq_len > 1 && demod.squelch_level == 0) { - fprintf(stderr, "Please specify a squelch level. Required for scanning multiple frequencies.\n"); - r = 0; - } - return r; -} - -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; - } - - if (!sanity_checks()) - return -1; - - 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/binding/rtl_fm_helper.c b/binding/rtl_fm_helper.c deleted file mode 100644 index c7df4b9..0000000 --- a/binding/rtl_fm_helper.c +++ /dev/null @@ -1,243 +0,0 @@ -/* - * Copyright (C) 2018 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include <stdio.h> -#include <stdlib.h> -#include <stdint.h> -#include <stdbool.h> -#include <string.h> -#include <glib.h> - -#include "rtl_fm.h" -#include "radio_output.h" - -#ifdef DEBUG -#define LOG_FILE "/tmp/helper.log" -FILE *_log; -#define LOG(...) \ - do { \ - if(!_log) _log = fopen(LOG_FILE, "w"); \ - fprintf(_log, __VA_ARGS__); \ - fflush(_log); \ - } while(0) -#else -#define LOG(...) do { } while(0) -#endif - -// Structure to describe FM band plans, all values in Hz. -typedef struct { - const 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 = 76000000, .max = 95000000, .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 char line[64]; - -static void rtl_output_callback(int16_t *result, int result_len, void *ctx) -{ - radio_output_write((char*) result, result_len * 2); -} - -static void rtl_freq_callback(uint32_t freq, void *ctx) -{ - // Note, need to flush output to ensure parent sees it - printf("F=%u\n", freq); - fflush(stdout); - LOG("F=%u\n", freq); -} - -static void rtl_scan_callback(uint32_t freq, void *ctx) -{ - // Note, need to flush output to ensure parent sees it - printf("S=%u\n", freq); - fflush(stdout); - LOG("S=%u\n", freq); -} - -static void read_config(void) -{ - GKeyFile* conf_file; - int conf_file_present = 0; - char *value_str; - - // 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); - - 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); - 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); - } -} - -int main(int argc, char *argv[]) -{ - int rc; - bool detect = false; - bool done = false; - bool started = false; - uint32_t frequency; - - LOG("started\n"); - - read_config(); - frequency = known_fm_band_plans[bandplan].min; - - if(argc == 2 && strcmp(argv[1], "--detect") == 0) { - detect = true; - } - - rc = rtl_fm_init(frequency, 200000, 48000, rtl_output_callback, NULL); - if(rc < 0) { - fprintf(stderr, "No RTL USB adapter?\n"); - exit(1); - } - if(detect) { - rtl_fm_cleanup(); - exit(0); - } - - rtl_fm_set_freq_callback(rtl_freq_callback, NULL); - - while(!done) { - LOG("Reading command\n"); - if (fgets(line, sizeof(line), stdin) == NULL) - break; - if(line[0] == '\0' || line[0] == '\n') - continue; - if(strcmp(line, "START\n") == 0) { - LOG("START received\n"); - if(!started) { - LOG("Starting\n"); - radio_output_start(); - rtl_fm_start(); - started = true; - } - } else if(strcmp(line, "STOP\n") == 0) { - LOG("STOP received\n"); - if(started) { - LOG("Stopping\n"); - radio_output_stop(); - rtl_fm_stop(); - started = false; - } - } else if(strncmp(line, "F=", 2) == 0) { - uint32_t n; - if(sscanf(line, "F=%u\n", &n) == 1) { - LOG("F=%d received\n", n); - rtl_fm_scan_stop(); - rtl_fm_set_freq(n); - } - } else if(strcmp(line, "S=UP\n") == 0) { - LOG("S=UP received\n"); - if(!started) - continue; - LOG("Calling rtl_fm_scan_start\n"); - rtl_fm_scan_start(0, - rtl_scan_callback, - NULL, - known_fm_band_plans[bandplan].step, - known_fm_band_plans[bandplan].min, - known_fm_band_plans[bandplan].max); - } else if(strcmp(line, "S=DOWN\n") == 0) { - LOG("S=DOWN received\n"); - if(!started) - continue; - LOG("Calling rtl_fm_scan_start\n"); - rtl_fm_scan_start(1, - rtl_scan_callback, - NULL, - known_fm_band_plans[bandplan].step, - known_fm_band_plans[bandplan].min, - known_fm_band_plans[bandplan].max); - } else if(strcmp(line, "S=STOP\n") == 0) { - LOG("S=STOP received\n"); - if(started) { - LOG("Calling rtl_fm_scan_stop\n"); - rtl_fm_scan_stop(); - } - } else if(line[0] == 'q' || line[0] == 'Q') - break; - } - if(started) { - radio_output_stop(); - rtl_fm_stop(); - } - rtl_fm_cleanup(); - LOG("done"); - return 0; -} diff --git a/binding/tef665x.h b/binding/tef665x.h deleted file mode 100644 index 10874e8..0000000 --- a/binding/tef665x.h +++ /dev/null @@ -1,397 +0,0 @@ -/* - * 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 TEF665X_H -#define TEF665X_H - -typedef uint8_t u8; -typedef uint16_t ushort; -typedef uint32_t uint; - -typedef enum -{ -_close = 0, -_open -} I2C_STATE; - -typedef enum -{ - TEF665X_MODULE_FM = 0x20, //32 - TEF665X_MODULE_AM = 0x21, //31 - TEF665X_MODULE_AUDIO = 0x30, //48 - TEF665X_MODULE_APPL = 0x40 //64 -} TEF665x_MODULE; - -enum { - TEF6657_CMD_tune = 0, - TEF6657_CMD_open = 1, - TEF6657_CMD_close = 2, - TEF6657_CMD_am = 3, - TEF6657_CMD_fm = 4, - TEF6657_CMD_GETamSTAUS = 5, - TEF6657_CMD_GETfmSTAUS = 6, - TEF6657_CMD_GEToirtSTAUS -}; - -enum -{ - RADIO_BOOT_STATE = 0, - RADIO_IDLE_STATE, - RADIO_STANBDY_STATE, - RADIO_FM_STATE, - RADIO_AM_STATE - -}; - -typedef enum -{ - TEF665X_Cmd_Set_OperationMode = 0x01, - TEF665X_Cmd_Set_GPIO = 0x03, - TEF665X_Cmd_Set_ReferenceClock = 0x04, - TEF665X_Cmd_Activate = 0x05, - - TEF665X_Cmd_Get_Operation_Status = 0x80, //128, - TEF665X_Cmd_Get_GPIO_Status = 0x81, //129, - TEF665X_Cmd_Get_Identification = 0x82, //130, - TEF665X_Cmd_Get_LastWrite = 0x83, //131 -} TEF665x_APPL_COMMAND; - -typedef enum -{ - TEF665X_Cmd_Set_RDS_mode = 0x01, // default - TEF665X_Cmd_Set_RDS_autorestart= 0x02, // restart after tune - TEF665X_Cmd_Set_RDS_interface = 0, // no interface -} TEF665x_FM_COMMAND; - -typedef enum -{ - TEF665X_Cmd_Tune_To = 0x01, - TEF665X_Cmd_Set_Tune_Options =0x02, - TEF665X_Cmd_Set_Bandwidth =0x0A, //10, - TEF665X_Cmd_Set_RFAGC =0x0B, //11, - TEF665X_Cmd_Set_Antenna =0x0C, //12, - - TEF665X_Cmd_Set_MphSuppression =0x14, //20, - TEF665X_Cmd_Set_NoiseBlanker =0x17, //23, - TEF665X_Cmd_Set_NoiseBlanker_Audio =0x18, //24, - - TEF665X_Cmd_Set_DigitalRadio =0x1E, //30, - TEF665X_Cmd_Set_Deemphasis =0x1F, //31, - - TEF665X_Cmd_Set_LevelStep = 0x26, //38, - TEF665X_Cmd_Set_LevelOffset = 0x27, //39, - - TEF665X_Cmd_Set_Softmute_Time =0x28, //40, - TEF665X_Cmd_Set_Softmute_Mod = 0x29, //41, - TEF665X_Cmd_Set_Softmute_Level =0x2A, //42, - TEF665X_Cmd_Set_Softmute_Noise =0x2B, //43, - TEF665X_Cmd_Set_Softmute_Mph =0x2C, //44, - TEF665X_Cmd_Set_Softmute_Max =0x2D, //45, - - TEF665X_Cmd_Set_Highcut_Time =0x32, //50, - TEF665X_Cmd_Set_Highcut_Mod = 0x33, //51, - TEF665X_Cmd_Set_Highcut_Level =0x34, //52, - TEF665X_Cmd_Set_Highcut_Noise = 0x35, //53, - TEF665X_Cmd_Set_Highcut_Mph =0x36, //54, - TEF665X_Cmd_Set_Highcut_Max =0x37, //55, - TEF665X_Cmd_Set_Highcut_Min =0x38, //56, - TEF665X_Cmd_Set_Lowcut_Min =0x3A, //58, - - TEF665X_Cmd_Set_Stereo_Time =0x3C, //60, - TEF665X_Cmd_Set_Stereo_Mod =0x3D, //61, - TEF665X_Cmd_Set_Stereo_Level =0x3E, //62, - TEF665X_Cmd_Set_Stereo_Noise = 0x3F, //63, - TEF665X_Cmd_Set_Stereo_Mph =0x40, //64, - TEF665X_Cmd_Set_Stereo_Max = 0x41, //65, - TEF665X_Cmd_Set_Stereo_Min = 0x42, //66, - - TEF665X_Cmd_Set_Scaler = 0x50, //80, - TEF665X_Cmd_Set_RDS = 0x51, //81, - TEF665X_Cmd_Set_QualityStatus = 0x52, //82, - TEF665X_Cmd_Set_DR_Blend = 0x53, //83, - TEF665X_Cmd_Set_DR_Options = 0x54, //84, - TEF665X_Cmd_Set_Specials = 0x55, //85, - - TEF665X_Cmd_Get_Quality_Status = 0x80, //128, - TEF665X_Cmd_Get_Quality_Data = 0x81, //129, - TEF665X_Cmd_Get_RDS_Status = 0x82, //130, - TEF665X_Cmd_Get_RDS_Data = 0x83, //131, - TEF665X_Cmd_Get_AGC = 0x84, //132, - TEF665X_Cmd_Get_Signal_Status = 0x85, //133, - TEF665X_Cmd_Get_Processing_Status = 0x86, //134, - TEF665X_Cmd_Get_Interface_Status = 0x87, //135, - - TEF665X_Cmd_Set_Output_signal_i2s = 0x21, //33, - TEF665X_Cmd_Set_Output_signal_dac = 0x80, //128, - TEF665X_Cmd_Set_Output_source_aRadio = 0x04, //4, - TEF665X_Cmd_Set_Output_source_dInput = 0x20, //32, - TEF665X_Cmd_Set_Output_source_aProcessor = 0xe0, //224, - TEF665X_Cmd_Set_Output_source_SinWave = 0xf0, //240, - } TEF665x_RADIO_COMMAND; - -typedef enum -{ - TEF665X_Cmd_Set_Volume = 0x0A, //10, - TEF665X_Cmd_Set_Mute = 0x0B, //11, - TEF665X_Cmd_Set_Input = 0x0C, //12, - TEF665X_Cmd_Set_Output_Source = 0x0D, //13, - - TEF665X_Cmd_Set_Ana_Out = 0x15, //21, - TEF665X_Cmd_Set_Dig_IO = 0x16, //22, - TEF665X_Cmd_Set_Input_Scaler = 0x17, //23, - TEF665X_Cmd_Set_WaveGen = 0x18, //24 -} TEF665x_AUDIO_COMMAND; - -typedef enum -{ - TEF665X_AUDIO_CMD_22_SIGNAL_i2s1 = 0x21, //33, - TEF665X_AUDIO_CMD_22_MODE_voltage = 0x02, //2, - TEF665X_AUDIO_CMD_22_FORMAT_16 = 0x10, //16, - TEF665X_AUDIO_CMD_22_FORMAT_32 = 0x20, //32, - TEF665X_AUDIO_CMD_22_OPERATION_slave = 0, //0, - TEF665X_AUDIO_CMD_22_OPERATION_master = 0x0100,//256, - TEF665X_AUDIO_CMD_22_SAMPLERATE_48K = 0x12c0 //4800 -} TEF665x_AUDIO_CMD_22; - -typedef enum{ - eDevTEF665x_Boot_state , - eDevTEF665x_Idle_state, - eDevTEF665x_Wait_Active, - eDevTEF665x_Active_state, - - eDevTEF665x_Power_on, - - eDevTEF665x_Not_Exist, - - eDevTEF665x_Last -}TEF665x_STATE; - -const u8 patchByteValues[]= -{ - 0xF0, 0x00, 0x38, 0x16, 0xD0, 0x80, 0x43, 0xB2, 0x38, 0x1D, 0xD0, 0x80, 0xF0, 0x00, 0x70, 0x00, 0xC2, 0xF7, 0xF0, 0x00, 0x38, 0x4E, 0xD0, 0x80, - 0xF0, 0x00, 0x38, 0xF1, 0xD0, 0x80, 0xC4, 0xA2, 0x02, 0x0C, 0x60, 0x04, 0x90, 0x01, 0x39, 0x07, 0xD0, 0x80, 0xF0, 0x00, 0x38, 0xD3, 0xD0, 0x80, - 0xF0, 0x00, 0x39, 0x0E, 0xD2, 0x80, 0xF0, 0x00, 0x39, 0x12, 0xD0, 0x80, 0x40, 0x20, 0x39, 0x1E, 0xD0, 0x80, 0x9E, 0x30, 0x18, 0xF9, 0xD2, 0x80, - 0xF0, 0x00, 0x39, 0x20, 0xD0, 0x80, 0xF0, 0x00, 0x39, 0x23, 0xD0, 0x80, 0xF0, 0x00, 0x39, 0x38, 0xD0, 0x80, 0xF0, 0x00, 0x39, 0x3B, 0xD0, 0x80, - 0x00, 0x43, 0x39, 0x43, 0xD9, 0x80, 0xF0, 0x00, 0x39, 0x46, 0xD0, 0x80, 0xF0, 0x00, 0x39, 0x60, 0xD0, 0x80, 0xF0, 0x00, 0x39, 0x71, 0xD0, 0x80, - 0xF0, 0x00, 0x39, 0x7F, 0xD0, 0x80, 0xF0, 0x00, 0x39, 0x82, 0xD0, 0x80, 0xF0, 0x00, 0x70, 0x00, 0xA0, 0x14, 0xF0, 0x00, 0x70, 0x00, 0xA0, 0xD4, - 0xF0, 0x00, 0x70, 0x00, 0xA0, 0xDB, 0xF0, 0x00, 0x70, 0x00, 0xA1, 0x0C, 0xF0, 0x00, 0x70, 0x00, 0xA1, 0x12, 0xF0, 0x00, 0x70, 0x00, 0xA1, 0x2D, - 0xF0, 0x00, 0x20, 0x31, 0xD0, 0x80, 0x00, 0x7F, 0x60, 0x02, 0xE2, 0x00, 0xF0, 0x00, 0x0E, 0x22, 0x60, 0x0A, 0xF0, 0x00, 0x00, 0xFF, 0x60, 0x03, - 0xF0, 0x00, 0x01, 0x42, 0xD2, 0x80, 0x90, 0x03, 0x40, 0x02, 0xF0, 0x00, 0x90, 0x43, 0x01, 0x70, 0xD1, 0x80, 0xF0, 0x00, 0x01, 0x69, 0xD0, 0x80, - 0x0E, 0x69, 0x60, 0x0A, 0xA1, 0x60, 0x20, 0x23, 0x00, 0x01, 0x60, 0x01, 0xF0, 0x00, 0x70, 0x00, 0xF0, 0x00, 0xC4, 0xCB, 0x70, 0x00, 0xF0, 0x00, - 0xCA, 0x09, 0x30, 0x23, 0xF0, 0x00, 0xC2, 0xCB, 0x70, 0x00, 0xF0, 0x00, 0xF0, 0x00, 0x30, 0x23, 0xD0, 0x08, 0x82, 0x00, 0x0D, 0x50, 0x60, 0x08, - 0xF0, 0x00, 0x0D, 0x51, 0x60, 0x09, 0x30, 0x00, 0x21, 0x80, 0x60, 0x01, 0xF0, 0x00, 0x40, 0x32, 0xF0, 0x00, 0x30, 0x11, 0x45, 0xF3, 0xF0, 0x00, - 0x30, 0x92, 0x2D, 0x30, 0x60, 0x04, 0x31, 0x13, 0x2D, 0x40, 0x60, 0x05, 0x31, 0x94, 0x7F, 0xFF, 0x60, 0x06, 0x32, 0x15, 0x0D, 0x61, 0x60, 0x0A, - 0x32, 0x96, 0x0D, 0x6B, 0x60, 0x0B, 0x33, 0x10, 0x0D, 0x50, 0x60, 0x01, 0x33, 0x90, 0x0D, 0x5C, 0x60, 0x02, 0x30, 0x21, 0x0D, 0x63, 0x60, 0x03, - 0x30, 0x31, 0x0D, 0x75, 0x60, 0x0C, 0x30, 0xA2, 0x8D, 0x00, 0x60, 0x01, 0x30, 0xB3, 0x01, 0x73, 0x60, 0x02, 0x30, 0x41, 0x00, 0x25, 0x60, 0x03, - 0x30, 0xC2, 0x40, 0x44, 0xF0, 0x00, 0x31, 0x43, 0x40, 0x35, 0xF0, 0x00, 0x31, 0xC4, 0x64, 0x00, 0x60, 0x06, 0x32, 0x45, 0x1F, 0x40, 0x60, 0x07, - 0x32, 0xC6, 0x70, 0x00, 0xF0, 0x00, 0x33, 0x47, 0x1E, 0xBC, 0x60, 0x0D, 0x33, 0xC0, 0x01, 0x22, 0x60, 0x01, 0x34, 0x40, 0xFD, 0xEE, 0x60, 0x02, - 0x30, 0x51, 0x7B, 0x8F, 0x60, 0x03, 0x30, 0xD2, 0xC4, 0x29, 0x60, 0x04, 0x31, 0x51, 0x1E, 0xC2, 0x60, 0x0E, 0x32, 0x53, 0xFF, 0x0D, 0x60, 0x02, - 0x32, 0xD4, 0x7D, 0x2E, 0x60, 0x03, 0x30, 0x61, 0xC1, 0x9A, 0x60, 0x04, 0x30, 0xE2, 0x70, 0x00, 0xF0, 0x00, 0x31, 0x61, 0x70, 0x00, 0xF0, 0x00, - 0x32, 0x63, 0x70, 0x00, 0xF0, 0x00, 0x32, 0xE4, 0x70, 0x00, 0xD0, 0x08, 0xF0, 0x00, 0x03, 0x70, 0xD2, 0x80, 0xF0, 0x00, 0x70, 0x00, 0xA0, 0x02, - 0xF0, 0x00, 0x70, 0x00, 0xA0, 0x59, 0xF0, 0x00, 0x02, 0x15, 0xD0, 0x80, 0xF0, 0x00, 0x0D, 0x51, 0x60, 0x0F, 0xF0, 0x00, 0x05, 0x17, 0x60, 0x0E, - 0x23, 0xF6, 0x70, 0x00, 0xF0, 0x00, 0xF0, 0x00, 0x21, 0x63, 0x41, 0xF5, 0x91, 0x8F, 0x21, 0xF8, 0x40, 0x74, 0xC3, 0xEF, 0x21, 0xE0, 0xF0, 0x00, - 0xC3, 0xA4, 0x33, 0xF7, 0xF0, 0x00, 0xD8, 0x5B, 0x70, 0x00, 0xF0, 0x00, 0x82, 0x18, 0x70, 0x00, 0xF0, 0x00, 0x9F, 0xAF, 0x18, 0x00, 0xF0, 0x00, - 0x9F, 0x0F, 0x31, 0xF8, 0x90, 0x02, 0xF0, 0x00, 0x70, 0x00, 0x90, 0x28, 0xF0, 0x00, 0x70, 0x00, 0xD0, 0x08, 0xF0, 0x00, 0x22, 0x78, 0xF0, 0x00, - 0x16, 0xD3, 0x60, 0x09, 0xA0, 0x7F, 0x35, 0xF0, 0x1E, 0xBC, 0x60, 0x0D, 0xF0, 0x00, 0x0D, 0x61, 0x60, 0x08, 0xF0, 0x00, 0x03, 0xA5, 0xD2, 0x80, - 0xF0, 0x00, 0x1E, 0xC2, 0x60, 0x0D, 0xF0, 0x00, 0x0D, 0x6B, 0x60, 0x08, 0xF0, 0x00, 0x03, 0xA5, 0xD2, 0x80, 0xF0, 0x00, 0x21, 0x00, 0xF0, 0x00, - 0x83, 0x6D, 0x22, 0xF1, 0xF0, 0x00, 0xF0, 0x00, 0x23, 0x77, 0xF0, 0x00, 0x90, 0x41, 0x36, 0x70, 0xF0, 0x00, 0x9E, 0x79, 0x70, 0x00, 0x90, 0x01, - 0xF0, 0x00, 0x32, 0xF1, 0xD0, 0x08, 0x91, 0xC7, 0x33, 0x75, 0xF0, 0x00, 0xF0, 0x00, 0x34, 0x70, 0xE6, 0x00, 0xF0, 0x00, 0x34, 0xF0, 0xE6, 0x00, - 0xF0, 0x00, 0x24, 0x74, 0xF0, 0x00, 0xF0, 0x00, 0x24, 0xF3, 0xF0, 0x00, 0x8C, 0x24, 0x26, 0xF2, 0x40, 0x16, 0x8A, 0x1B, 0x34, 0x74, 0x4F, 0xF5, - 0x82, 0xB7, 0x34, 0xF3, 0xF0, 0x00, 0xF0, 0x00, 0x20, 0x71, 0x90, 0x05, 0x83, 0x04, 0x70, 0x00, 0xF0, 0x00, 0x8E, 0x67, 0x70, 0x00, 0xF0, 0x00, - 0xF0, 0x00, 0x70, 0x00, 0x90, 0x02, 0xF0, 0x00, 0x36, 0xF6, 0xF0, 0x00, 0xF0, 0x00, 0x34, 0xF0, 0x80, 0x06, 0x82, 0xAF, 0x70, 0x00, 0xF0, 0x00, - 0x82, 0x1B, 0x70, 0x00, 0xD0, 0x09, 0x8E, 0x5F, 0x70, 0x00, 0xF0, 0x00, 0xF0, 0x00, 0x70, 0x00, 0xD0, 0x09, 0xF0, 0x00, 0x36, 0xF5, 0xF0, 0x00, - 0xF0, 0x00, 0x34, 0x70, 0xF0, 0x00, 0x40, 0x11, 0x27, 0x72, 0xA1, 0x03, 0x90, 0x8A, 0x20, 0xF3, 0xA1, 0x02, 0x8E, 0xD7, 0x37, 0x72, 0xF0, 0x00, - 0xF0, 0x00, 0x37, 0xF1, 0xE6, 0x00, 0xF0, 0x00, 0x70, 0x00, 0xD0, 0x08, 0xF0, 0x00, 0x22, 0x7A, 0xF0, 0x00, 0x16, 0xC3, 0x60, 0x09, 0xA0, 0x58, - 0xF0, 0x00, 0x18, 0x20, 0xF0, 0x00, 0xF0, 0x00, 0x35, 0x70, 0xF0, 0x00, 0xF0, 0x00, 0x32, 0x7A, 0xD0, 0x08, 0x82, 0x00, 0x0D, 0x51, 0x60, 0x08, - 0x40, 0x03, 0x70, 0x00, 0xF0, 0x00, 0x33, 0x80, 0x70, 0x00, 0xF0, 0x00, 0x21, 0x06, 0x70, 0x00, 0xF0, 0x00, 0x37, 0x00, 0x70, 0x00, 0xF0, 0x00, - 0x37, 0x80, 0x40, 0x15, 0xF0, 0x00, 0x36, 0x83, 0x70, 0x00, 0xF0, 0x00, 0x33, 0x05, 0x0D, 0x61, 0x60, 0x09, 0x32, 0x86, 0x0D, 0x6B, 0x60, 0x0A, - 0x32, 0x10, 0x70, 0x00, 0xF0, 0x00, 0x32, 0x90, 0x70, 0x00, 0xF0, 0x00, 0x33, 0x10, 0x70, 0x00, 0xF0, 0x00, 0x33, 0x90, 0x70, 0x00, 0xF0, 0x00, - 0x34, 0x10, 0x70, 0x00, 0xF0, 0x00, 0x34, 0x90, 0x70, 0x00, 0xF0, 0x00, 0x31, 0x10, 0x70, 0x00, 0xF0, 0x00, 0x31, 0x90, 0x70, 0x00, 0xF0, 0x00, - 0x32, 0x20, 0x70, 0x00, 0xF0, 0x00, 0x32, 0xA0, 0x70, 0x00, 0xF0, 0x00, 0x33, 0x20, 0x70, 0x00, 0xF0, 0x00, 0x33, 0xA0, 0x70, 0x00, 0xF0, 0x00, - 0x34, 0x20, 0x70, 0x00, 0xF0, 0x00, 0x34, 0xA0, 0x70, 0x00, 0xF0, 0x00, 0x31, 0x20, 0x70, 0x00, 0xF0, 0x00, 0x31, 0xA0, 0x70, 0x00, 0xF0, 0x00, - 0x82, 0x00, 0x0D, 0x30, 0x60, 0x0A, 0x0D, 0x40, 0x60, 0x0B, 0xC0, 0x10, 0xF0, 0x00, 0x10, 0x20, 0xF0, 0x00, 0x0D, 0x51, 0x60, 0x0C, 0xC0, 0x10, - 0xF0, 0x00, 0x10, 0x30, 0xF0, 0x00, 0xF0, 0x00, 0x35, 0xC0, 0xD0, 0x08, 0xF0, 0x00, 0x0D, 0x75, 0x60, 0x0F, 0xF0, 0x00, 0x05, 0x63, 0x60, 0x0E, - 0x24, 0xF7, 0x05, 0x1D, 0x60, 0x0D, 0x25, 0x76, 0x70, 0x00, 0xF0, 0x00, 0x91, 0xC7, 0x20, 0xE8, 0x40, 0x15, 0x91, 0x8F, 0x21, 0xE9, 0xD4, 0x09, - 0xC3, 0xEF, 0x20, 0x00, 0x40, 0x12, 0x9F, 0xBE, 0x20, 0x11, 0x58, 0x03, 0xA0, 0x80, 0x35, 0x77, 0x90, 0x01, 0xF0, 0x00, 0x70, 0x00, 0xD0, 0x08, - 0xF0, 0x00, 0x21, 0xF5, 0xF0, 0x00, 0xA0, 0xCA, 0x22, 0x54, 0xF0, 0x00, 0xCC, 0x09, 0x05, 0x17, 0x60, 0x0C, 0x83, 0x2C, 0x70, 0x00, 0xF0, 0x00, - 0x8A, 0x61, 0x70, 0x00, 0xF0, 0x00, 0xAE, 0x48, 0x22, 0x45, 0xA0, 0xCB, 0xA2, 0x28, 0x20, 0x78, 0xF0, 0x00, 0xF0, 0x00, 0x35, 0xF0, 0xF0, 0x00, - 0xF0, 0x00, 0x18, 0x00, 0xF0, 0x00, 0xF0, 0x00, 0x30, 0x78, 0xF0, 0x00, 0x16, 0xE3, 0x60, 0x09, 0xA0, 0x27, 0x89, 0x01, 0x23, 0xF4, 0xF0, 0x00, - 0xF0, 0x00, 0x20, 0xF2, 0xF0, 0x00, 0x82, 0x61, 0x21, 0x73, 0xF0, 0x00, 0xA0, 0x50, 0x36, 0x70, 0xF0, 0x00, 0xA0, 0x58, 0x23, 0x72, 0xE1, 0x40, - 0xA8, 0x01, 0x22, 0xF3, 0xF0, 0x00, 0x90, 0x49, 0x22, 0x75, 0xE0, 0x40, 0x80, 0x61, 0x70, 0x00, 0xF0, 0x00, 0x8A, 0x51, 0x33, 0xF1, 0xF0, 0x00, - 0xA0, 0x58, 0x70, 0x00, 0xF0, 0x00, 0xAF, 0x48, 0x70, 0x00, 0xF0, 0x00, 0xF0, 0x00, 0x34, 0x70, 0xD0, 0x08, 0x82, 0x00, 0x0D, 0x75, 0x60, 0x08, - 0x90, 0x09, 0x0D, 0x00, 0x60, 0x09, 0xF0, 0x00, 0x35, 0x00, 0xF0, 0x00, 0xF0, 0x00, 0x33, 0x80, 0xC0, 0x28, 0xF0, 0x00, 0x10, 0x10, 0xF0, 0x00, - 0xF0, 0x00, 0x34, 0x81, 0xD0, 0x08, 0x82, 0x49, 0x0D, 0x75, 0x60, 0x08, 0xF0, 0x00, 0x70, 0x00, 0x8F, 0xFD, 0x04, 0x00, 0x60, 0x00, 0xA0, 0xB1, - 0x8E, 0xC0, 0x40, 0x00, 0x60, 0x05, 0x60, 0x00, 0x60, 0x05, 0xE6, 0x00, 0xC8, 0x1B, 0x70, 0x00, 0xF0, 0x00, 0xD8, 0xDB, 0x0D, 0x51, 0x60, 0x08, - 0x83, 0x5B, 0x70, 0x00, 0xF0, 0x00, 0x9E, 0xBA, 0x30, 0x03, 0xF0, 0x00, 0xF0, 0x00, 0x30, 0x84, 0xD4, 0x09, 0xF0, 0x00, 0x70, 0x00, 0x8F, 0xAF, - 0xF0, 0x00, 0x0D, 0x75, 0x60, 0x08, 0xF0, 0x00, 0x0D, 0x51, 0x60, 0x09, 0xF0, 0x00, 0x24, 0x03, 0xF0, 0x00, 0xF0, 0x00, 0x27, 0x94, 0xD0, 0x08, - 0xA0, 0x03, 0x70, 0x00, 0xF0, 0x00, 0x00, 0x11, 0x08, 0x00, 0xF0, 0x00, 0x00, 0x11, 0x08, 0x00, 0xC0, 0x0E, 0xA0, 0x09, 0x00, 0x11, 0x08, 0x00, - 0xA0, 0x09, 0x70, 0x00, 0xF0, 0x00, 0xA4, 0x08, 0x70, 0x00, 0xD0, 0x08, 0xA0, 0x03, 0x70, 0x00, 0xF0, 0x00, 0x00, 0x11, 0x08, 0x00, 0xF0, 0x00, - 0x00, 0x11, 0x08, 0x00, 0xC0, 0x26, 0xA0, 0x09, 0x00, 0x11, 0x08, 0x00, 0xA0, 0x09, 0x70, 0x00, 0xF0, 0x00, 0xA4, 0x08, 0x70, 0x00, 0xD0, 0x08, - 0xF0, 0x00, 0x1D, 0x01, 0x60, 0x08, 0xF0, 0x00, 0x0A, 0x2C, 0x60, 0x00, 0xF0, 0x00, 0x01, 0x1A, 0x60, 0x01, 0x31, 0x00, 0x70, 0x00, 0xF0, 0x00, - 0x31, 0x81, 0x70, 0x00, 0xD0, 0x08, 0x10, 0x00, 0x60, 0x03, 0xA0, 0x93, 0x30, 0x23, 0x07, 0x73, 0xD2, 0x80, 0xF0, 0x00, 0x07, 0xC6, 0xD0, 0x80, - 0x40, 0xE0, 0x00, 0x1F, 0x60, 0x01, 0x13, 0xD5, 0x60, 0x07, 0xA0, 0x06, 0x13, 0xFB, 0x60, 0x06, 0xF0, 0x00, 0x90, 0x40, 0x0D, 0x28, 0xD2, 0x80, - 0x14, 0x05, 0x60, 0x06, 0xF0, 0x00, 0xF0, 0x00, 0x0D, 0x28, 0xD2, 0x80, 0x14, 0x0F, 0x60, 0x06, 0xF0, 0x00, 0xF0, 0x00, 0x0D, 0x28, 0xD0, 0x80, - 0xD7, 0xCA, 0x00, 0xFF, 0x60, 0x04, 0x81, 0xD7, 0x0C, 0xF7, 0x60, 0x09, 0xD0, 0x56, 0x70, 0x00, 0xF0, 0x00, 0x82, 0x76, 0x30, 0x17, 0xF0, 0x00, - 0xD0, 0xF6, 0x40, 0x83, 0xF0, 0x00, 0xC1, 0xA4, 0x20, 0x19, 0xF0, 0x00, 0x82, 0xF6, 0x70, 0x00, 0xF0, 0x00, 0xC1, 0x80, 0x20, 0x17, 0xA0, 0x81, - 0xC3, 0xE7, 0x70, 0x00, 0xF0, 0x00, 0xC5, 0xC7, 0x70, 0x00, 0xF0, 0x00, 0xF0, 0x00, 0x10, 0xB1, 0xD0, 0x80, 0x40, 0x00, 0x70, 0x00, 0xAF, 0xF0, - 0x41, 0xF1, 0x40, 0x40, 0xF0, 0x00, 0xF0, 0x00, 0x10, 0xB2, 0xD0, 0x80, 0x40, 0x71, 0x10, 0xB2, 0xD2, 0x80, 0xF0, 0x00, 0x10, 0x81, 0xD0, 0x80, - 0xF0, 0x00, 0x0B, 0xC9, 0x60, 0x08, 0xF0, 0x00, 0x1D, 0x8D, 0xD2, 0x80, 0xF0, 0x00, 0x16, 0xD5, 0xD0, 0x80, 0xF0, 0x00, 0x0B, 0xC9, 0x60, 0x08, - 0xF0, 0x00, 0x1D, 0x8F, 0xD2, 0x80, 0xF0, 0x00, 0x16, 0xDA, 0xD0, 0x80, 0xF0, 0x00, 0x0B, 0x60, 0x60, 0x0E, 0xF0, 0x00, 0x00, 0x04, 0x60, 0x03, - 0xF0, 0x00, 0x3F, 0xFC, 0x60, 0x04, 0x32, 0x63, 0x80, 0x08, 0x60, 0x05, 0x32, 0xE4, 0x19, 0x9A, 0x60, 0x06, 0x33, 0x65, 0x70, 0x00, 0xF0, 0x00, - 0x31, 0xE6, 0x70, 0x00, 0xD0, 0x08, 0x83, 0x6D, 0x0C, 0x35, 0x60, 0x08, 0x40, 0x60, 0x39, 0x36, 0x60, 0x01, 0x41, 0xE2, 0x21, 0x96, 0x60, 0x03, - 0x33, 0x00, 0x41, 0x44, 0xF0, 0x00, 0x33, 0x81, 0x70, 0x00, 0xF0, 0x00, 0x34, 0x02, 0x70, 0x00, 0xF0, 0x00, 0x34, 0x83, 0x70, 0x00, 0xF0, 0x00, - 0x35, 0x04, 0x70, 0x00, 0xF0, 0x00, 0x35, 0x85, 0x70, 0x00, 0xD0, 0x08, 0xF0, 0x00, 0x70, 0x00, 0xAF, 0x54, 0xF0, 0x00, 0x70, 0x00, 0x8F, 0x99, - 0xF0, 0x00, 0x70, 0x00, 0xAF, 0x92, 0xF0, 0x00, 0x0C, 0x51, 0xD2, 0x80, 0xF0, 0x00, 0x21, 0xA0, 0xD0, 0x80, 0xF0, 0x00, 0x05, 0x2E, 0xD2, 0x80, - 0xF0, 0x00, 0x70, 0x00, 0xA0, 0x01, 0xF0, 0x00, 0x21, 0xB7, 0xD0, 0x80, 0xF0, 0x00, 0x20, 0xF0, 0xD2, 0x80, 0x90, 0x02, 0x27, 0xDF, 0xD2, 0x80, - 0x9E, 0x69, 0x70, 0x00, 0xF0, 0x00, 0xF0, 0x00, 0x07, 0x00, 0xD1, 0x80, 0xF0, 0x00, 0x23, 0x20, 0xD0, 0x80, 0x17, 0x0B, 0x60, 0x0C, 0xA0, 0x41, - 0x00, 0x40, 0x40, 0x05, 0xF0, 0x00, 0x00, 0x41, 0x24, 0x3F, 0xD0, 0x80, 0xF0, 0x00, 0x70, 0x00, 0xAE, 0xDD, 0xF0, 0x00, 0x0C, 0x8D, 0x60, 0x08, - 0xF0, 0x00, 0x26, 0x0A, 0xD0, 0x80, 0x83, 0xFF, 0x0D, 0x83, 0x60, 0x08, 0xF0, 0x00, 0x01, 0xF4, 0x60, 0x00, 0xF0, 0x00, 0x03, 0xB1, 0x60, 0x01, - 0x10, 0x00, 0x03, 0xB2, 0x60, 0x02, 0x10, 0x01, 0x04, 0x0E, 0x60, 0x00, 0x10, 0x02, 0x04, 0x0F, 0x60, 0x01, 0x10, 0x00, 0x04, 0x5C, 0x60, 0x02, - 0x10, 0x01, 0x04, 0x5D, 0x60, 0x00, 0x10, 0x02, 0x13, 0x80, 0x60, 0x01, 0x10, 0x00, 0x0D, 0x8E, 0x60, 0x09, 0x10, 0x01, 0x02, 0xEE, 0x60, 0x00, - 0x10, 0x07, 0x43, 0x06, 0x60, 0x01, 0x10, 0x10, 0x04, 0x69, 0x60, 0x02, 0x10, 0x11, 0x44, 0x87, 0x60, 0x00, 0x10, 0x12, 0x05, 0xE3, 0x60, 0x01, - 0x10, 0x10, 0x46, 0x08, 0x60, 0x02, 0x10, 0x11, 0x06, 0xAE, 0x60, 0x00, 0x10, 0x12, 0x0D, 0x8C, 0x60, 0x08, 0x10, 0x10, 0x9E, 0x3C, 0x60, 0x00, - 0x10, 0x17, 0x0D, 0x82, 0x60, 0x09, 0x10, 0x00, 0x70, 0x00, 0xF0, 0x00, 0x10, 0x07, 0x70, 0x00, 0xF0, 0x00, 0x10, 0x17, 0x70, 0x00, 0xD0, 0x08, - 0x0D, 0x83, 0x60, 0x08, 0xA0, 0x24, 0xF0, 0x00, 0x00, 0x02, 0xA0, 0x23, 0x90, 0x82, 0x70, 0x00, 0xF0, 0x00, 0x82, 0x8A, 0x70, 0x00, 0x90, 0x03, - 0x90, 0x8A, 0x70, 0x00, 0x90, 0x01, 0xF0, 0x00, 0x70, 0x00, 0x8F, 0xFB, 0x82, 0xBF, 0x70, 0x00, 0xF0, 0x00, 0xF0, 0x00, 0x01, 0x99, 0xD2, 0x80, - 0xF0, 0x00, 0x0D, 0x82, 0x60, 0x08, 0xF0, 0x00, 0x26, 0xC1, 0xF0, 0x00, 0xF0, 0x00, 0x00, 0x02, 0xA0, 0x1A, 0x90, 0x82, 0x70, 0x00, 0xF0, 0x00, - 0x82, 0x8A, 0x70, 0x00, 0x90, 0x03, 0x90, 0x8A, 0x70, 0x00, 0x90, 0x01, 0xF0, 0x00, 0x70, 0x00, 0x8F, 0xFB, 0x82, 0x80, 0x70, 0x00, 0xF0, 0x00, - 0xF0, 0x00, 0x70, 0x00, 0xD0, 0x08, 0xF0, 0x00, 0x0D, 0x8E, 0x60, 0x0D, 0xF0, 0x00, 0x3F, 0xFF, 0x60, 0x02, 0xF0, 0x00, 0x00, 0x51, 0xA0, 0x11, - 0xC2, 0x53, 0x70, 0x00, 0xF0, 0x00, 0x8E, 0xC4, 0x70, 0x00, 0x90, 0x01, 0xF0, 0x00, 0x70, 0x00, 0x97, 0xFC, 0xD4, 0x8F, 0x01, 0x99, 0xD2, 0x80, - 0x40, 0x05, 0x0D, 0x8C, 0x60, 0x0D, 0x40, 0x17, 0x3F, 0xFF, 0x60, 0x02, 0x9F, 0x7D, 0x00, 0x51, 0xA0, 0x0A, 0xC2, 0x53, 0x70, 0x00, 0xF0, 0x00, - 0x82, 0xC4, 0x27, 0x1C, 0xD1, 0x80, 0xF0, 0x00, 0x70, 0x00, 0x97, 0xFC, 0x91, 0x46, 0x27, 0x20, 0xD0, 0x80, 0xF0, 0x00, 0x29, 0x15, 0xD2, 0x80, - 0xF0, 0x00, 0x39, 0x86, 0xD2, 0x80, 0xF0, 0x00, 0x28, 0x88, 0xD0, 0x80, 0x01, 0x52, 0x60, 0x00, 0xF0, 0x00, 0xF0, 0x00, 0x28, 0xDC, 0xD2, 0x80, - 0xF0, 0x00, 0x28, 0xD5, 0xD0, 0x80, 0xF0, 0x00, 0x70, 0x00, 0xD0, 0x08, 0xF0, 0x00, 0x16, 0xC3, 0x60, 0x08, 0xF0, 0x00, 0x00, 0x9A, 0x60, 0x00, - 0xF0, 0x00, 0x02, 0xE3, 0x60, 0x00, 0x10, 0x00, 0x04, 0xF4, 0x60, 0x00, 0x10, 0x00, 0x06, 0xFF, 0x60, 0x00, 0x10, 0x00, 0x09, 0x07, 0x60, 0x00, - 0x10, 0x00, 0x0B, 0x10, 0x60, 0x00, 0x10, 0x00, 0x0D, 0x1F, 0x60, 0x00, 0x10, 0x00, 0x0F, 0x65, 0x60, 0x00, 0x10, 0x00, 0x0F, 0x65, 0x60, 0x00, - 0x10, 0x00, 0x0D, 0x1F, 0x60, 0x00, 0x10, 0x00, 0x0B, 0x10, 0x60, 0x00, 0x10, 0x00, 0x09, 0x07, 0x60, 0x00, 0x10, 0x00, 0x06, 0xFF, 0x60, 0x00, - 0x10, 0x00, 0x04, 0xF4, 0x60, 0x00, 0x10, 0x00, 0x02, 0xE3, 0x60, 0x00, 0x10, 0x00, 0x00, 0x9A, 0x60, 0x00, 0xF0, 0x00, 0x10, 0x00, 0xF0, 0x00, - 0xF0, 0x00, 0x10, 0x00, 0xF0, 0x00, 0xF0, 0x00, 0x16, 0xD3, 0x60, 0x08, 0xF0, 0x00, 0xFF, 0x93, 0x60, 0x00, 0xF0, 0x00, 0xFE, 0x37, 0x60, 0x00, - 0x10, 0x00, 0xFD, 0xD9, 0x60, 0x00, 0x10, 0x00, 0xFE, 0x9F, 0x60, 0x00, 0x10, 0x00, 0x03, 0x85, 0x60, 0x00, 0x10, 0x00, 0x0D, 0x6B, 0x60, 0x00, - 0x10, 0x00, 0x16, 0x84, 0x60, 0x00, 0x10, 0x00, 0x1E, 0x49, 0x60, 0x00, 0x10, 0x00, 0x1E, 0x49, 0x60, 0x00, 0x10, 0x00, 0x16, 0x84, 0x60, 0x00, - 0x10, 0x00, 0x0D, 0x6B, 0x60, 0x00, 0x10, 0x00, 0x03, 0x85, 0x60, 0x00, 0x10, 0x00, 0xFE, 0x9F, 0x60, 0x00, 0x10, 0x00, 0xFD, 0xD9, 0x60, 0x00, - 0x10, 0x00, 0xFE, 0x37, 0x60, 0x00, 0x10, 0x00, 0xFF, 0x93, 0x60, 0x00, 0xF0, 0x00, 0x10, 0x00, 0xF0, 0x00, 0xF0, 0x00, 0x10, 0x00, 0xF0, 0x00, - 0xF0, 0x00, 0x16, 0xE3, 0x60, 0x08, 0xF0, 0x00, 0x00, 0x64, 0x60, 0x00, 0xF0, 0x00, 0xFF, 0xA8, 0x60, 0x00, 0x10, 0x00, 0xFF, 0xA6, 0x60, 0x00, - 0x10, 0x00, 0xFF, 0xDF, 0x60, 0x00, 0x10, 0x00, 0x00, 0x01, 0x60, 0x00, 0x10, 0x00, 0x01, 0x28, 0x60, 0x00, 0x10, 0x00, 0x01, 0x2F, 0x60, 0x00, - 0x10, 0x00, 0xFD, 0x23, 0x60, 0x00, 0x10, 0x00, 0xFD, 0xA1, 0x60, 0x00, 0x10, 0x00, 0x03, 0x89, 0x60, 0x00, 0x10, 0x00, 0x02, 0x54, 0x60, 0x00, - 0x10, 0x00, 0x0E, 0xA2, 0x60, 0x00, 0x10, 0x00, 0x0C, 0x47, 0x60, 0x00, 0x10, 0x00, 0xF9, 0x42, 0x60, 0x00, 0x10, 0x00, 0xFB, 0xE1, 0x60, 0x00, - 0x10, 0x00, 0x00, 0x8C, 0x60, 0x00, 0x10, 0x00, 0xFE, 0x7D, 0x60, 0x00, 0x10, 0x00, 0x02, 0x54, 0x60, 0x00, 0x10, 0x00, 0x03, 0x89, 0x60, 0x00, - 0x10, 0x00, 0xFD, 0xA1, 0x60, 0x00, 0x10, 0x00, 0xFD, 0x23, 0x60, 0x00, 0x10, 0x00, 0x01, 0x2F, 0x60, 0x00, 0x10, 0x00, 0x01, 0x28, 0x60, 0x00, - 0x10, 0x00, 0x00, 0x01, 0x60, 0x00, 0x10, 0x00, 0xFF, 0xDF, 0x60, 0x00, 0x10, 0x00, 0xFF, 0xA6, 0x60, 0x00, 0x10, 0x00, 0xFF, 0xA8, 0x60, 0x00, - 0x10, 0x00, 0x00, 0x64, 0x60, 0x00, 0xF0, 0x00, 0x10, 0x00, 0xF0, 0x00, 0xF0, 0x00, 0x10, 0x00, 0xF0, 0x00, 0xF0, 0x00, 0x17, 0x0B, 0x60, 0x08, - 0xF0, 0x00, 0x00, 0x03, 0x60, 0x00, 0xF0, 0x00, 0x54, 0xC0, 0x60, 0x00, 0x10, 0x00, 0x00, 0x05, 0x60, 0x00, 0x10, 0x00, 0x00, 0x05, 0x60, 0x00, - 0x10, 0x00, 0x00, 0x0F, 0x60, 0x00, 0x10, 0x00, 0x00, 0x0F, 0x60, 0x00, 0x10, 0x00, 0x09, 0xC0, 0x60, 0x00, 0x10, 0x00, 0x0A, 0x20, 0x60, 0x00, - 0x10, 0x00, 0x1D, 0x40, 0x60, 0x00, 0x10, 0x00, 0x1E, 0x60, 0x60, 0x00, 0xF0, 0x00, 0x10, 0x00, 0xF0, 0x00, 0xF0, 0x00, 0x10, 0x00, 0xF0, 0x00, - 0xF0, 0x00, 0x70, 0x00, 0xD0, 0x08 -}; - -unsigned char lutByteValues[]= -{ - /* - 0x40, 0x13, 0x41, 0x68, 0x41, 0xC1, 0x42, 0x14, - 0x47, 0xC5, 0x4D, 0x83, 0x4E, 0x49, 0x4E, 0x53, - 0x4F, 0x92, 0x4F, 0xEA, 0x50, 0x80, 0x56, 0x60, - 0x56, 0xD4, 0x56, 0xD9, 0x61, 0x9F, 0x61, 0xB6, - 0x64, 0x3B, 0x66, 0x09, 0x67, 0x0B, 0x67, 0x1A, - 0x68, 0x87, 0x68, 0xD4 - */ - 0x80, 0x17, 0x80, 0x45, 0x80, 0x96, 0x81, 0x5B, - 0x82, 0xD6, 0x88, 0x0B, 0x88, 0x10, 0x88, 0xDB, - 0x89, 0x48, 0x8B, 0x0A, 0x8B, 0x12, 0x8B, 0x13, - 0x8B, 0x21, 0x8B, 0x26, 0x8C, 0x6F, 0x94, 0xD0, - 0x95, 0x20, 0x95, 0x33, 0x95, 0x6F, 0x95, 0xA8, - 0x95, 0xAC, 0x95, 0xC3, 0x95, 0xE1, 0x97, 0xC9, - 0x97, 0xF6, 0x98, 0x8D, 0x99, 0x00, 0x9A, 0x00, - 0x9E, 0x73, 0xA0, 0x12, 0xA1, 0x03, 0xA2, 0xF0, - 0xA3, 0x0D, 0xA3, 0x4C, 0xA3, 0x52, 0xA3, 0xED, - 0xA8, 0x31, 0xAB, 0x01, 0xAB, 0x1F, 0xAB, 0x4C, - 0xAC, 0x76, 0xAC, 0x88, 0xAD, 0xB0, 0xAD, 0xB7, - 0xB0, 0xE2, 0xB1, 0x01, 0xB1, 0x0B, 0xB1, 0x35, - 0xB1, 0x3B, 0xB1, 0x97, 0xB3, 0x03 -}; - -const uint32_t patchSize = sizeof(patchByteValues); -const uint8_t *pPatchBytes = &patchByteValues[0]; - -const uint32_t lutSize = sizeof(lutByteValues); -const uint8_t *pLutBytes = &lutByteValues[0]; - -static const u8 init_para[] = { -//Set the Band related API settings... -11, 0x20, 0x0A, 0x01, 0x00, 0x01, 0x09, 0x38, 0x05, 0xDC, 0x05, 0xDC, //FM_BandWidth Auto, FM_Set_Bandwidth (1, 1, 2360, 1500, 1200) -5, 0x20, 0x14, 0x01, 0x00, 0x01, //FM_MphSuppression, FM_Set_MphSuppression (1, 1) -//5, 0x20, 0x16, 0x01, 0x00, 0x01, -7, 0x20, 0x17, 0x01, 0x00, 0x01, 0x05, 0xDC, //FM_NoiseBlanker, FM_Set_NoiseBlanker (1, 1, 1200) - -//Set all Weaksignal API settings (LevelOffset)... -//Set the SoftMute API settings... -9, 0x20, 0x2A, 0x01, 0x00, 0x03, 0x00, 0x64, 0x00, 0xFA, //FM_SmlMode, FM_Set_SoftMute_Level (1, 3, 100, 250) -11, 0x20, 0x28, 0x01, 0x00, 0x3C, 0x00, 0x78, 0x00, 0x0A, 0x00, 0x14, //FM_SmSlowAttack,FM_Set_SoftMute_Time (1, 60, 120, 10, 20) -9, 0x20, 0x2C, 0x01, 0x00, 0x03, 0x01, 0x90, 0x03, 0xE8, //FM_Smm,FM_Set_SoftMute_Mph (1, 3, 400, 1000) -9, 0x20, 0x2B, 0x01, 0x00, 0x03, 0x01, 0x90, 0x03, 0xE8, //FM_Smn,FM_Set_SoftMute_Noise (1, 3, 400, 1000) -7, 0x20, 0x2D, 0x01, 0x00, 0x01, 0x00, 0x64, //FM_SmMaximum,FM_Set_SoftMute_Max (1, 1, 100) - -//Set the HighCut API settings... -9, 0x20, 0x34, 0x01, 0x00, 0x01, 0x01, 0xF4, 0x00, 0xC8, //FM_HclMode,FM_Set_HighCut_Level (1, 1, 500, 200) -11, 0x20, 0x32, 0x01, 0x00, 0x3C, 0x00, 0x78, 0x00, 0x14, 0x00, 0x14, //FM_HcSlowAttack,FM_Set_HighCut_Time (1, 60, 120, 20, 20) -11, 0x20, 0x33, 0x01, 0x00, 0x01, 0x01, 0x90, 0x00, 0xC8, 0x03, 0x20, //FM_Hco,FM_Set_HighCut_Mod (1, 1, 400, 200, 800) -9, 0x20, 0x36, 0x01, 0x00, 0x01, 0x00, 0x64, 0x00, 0x64, //FM_Hcm,FM_Set_HighCut_Mph (1, 1, 100, 100) -9, 0x20, 0x35, 0x01, 0x00, 0x01, 0x00, 0x64, 0x00, 0x64, //FM_Hcn,FM_Set_HighCut_Noise (1, 1, 100, 100) -7, 0x20, 0x38, 0x01, 0x00, 0x01, 0x3A, 0x98, //FM_HcMinimum,FM_Set_HighCut_Min (1, 1, 15000) -7, 0x20, 0x3A, 0x01, 0x00, 0x01, 0x00, 0x0A, //FM_HcLowCutMinimum,FM_Set_LowCut_Min (1, 1, 100) -7, 0x20, 0x37, 0x01, 0x00, 0x01, 0x05, 0xDC, //FM_HcMaximum,FM_Set_HighCut_Max (1, 1, 1500) - -//Set the Stereo API settings... -9, 0x20, 0x3E, 0x01, 0x00, 0x01, 0x01, 0xF4, 0x00, 0xFA, //FM_StlMode,FM_Set_Stereo_Level (1, 1, 500, 250) -11, 0x20, 0x3C, 0x01, 0x00, 0x3C, 0x00, 0x78, 0x00, 0x0A, 0x00, 0x14, //FM_StSlowAttack,FM_Set_Stereo_Time (1, 60, 120, 10, 20) -11, 0x20, 0x3D, 0x01, 0x00, 0x01, 0x00, 0xC8, 0x00, 0xC8, 0x03, 0xE8, //FM_Sto,FM_Set_Stereo_Mod (1, 1, 200, 200, 1000) -9, 0x20, 0x40, 0x01, 0x00, 0x01, 0x00, 0x64, 0x00, 0x64, //FM_Stm,FM_Set_Stereo_Mph (1, 1, 100, 100) -9, 0x20, 0x3F, 0x01, 0x00, 0x01, 0x00, 0x64, 0x00, 0x64, //FM_Stn,FM_Set_Stereo_Noise (1, 1, 100, 100) -7, 0x20, 0x39, 0x01, 0x00, 0x01, 0x00, 0x50, -9, 0x20, 0x4A, 0x01, 0x00, 0x03, 0x00, 0x50, 0x00, 0x80, -9, 0x20, 0x49, 0x01, 0x00, 0x03, 0x00, 0x50, 0x00, 0x80, - -//Set_Deemphasis -5, 0x20, 0x1F, 0x01, 0x02, 0xEE, - -//Set RDS -9, 0x20, 0x51, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x02, - -//Set the Audio and Application related API settings... -9, 0x40, 0x03, 0x01, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, //AM_GPIO 0 Feature -9, 0x40, 0x03, 0x01, 0x00, 0x01, 0x00, 0x21, 0x00, 0x00, //AM_GPIO 1 Feature -9, 0x40, 0x03, 0x01, 0x00, 0x02, 0x00, 0x21, 0x00, 0x00, //AM_GPIO 2 Feature -9, 0x40, 0x03, 0x01, 0x00, 0x00, 0x00, 0x20, 0x01, 0x01, //FM_GPIO 0 Feature set for RDS -9, 0x40, 0x03, 0x01, 0x00, 0x01, 0x00, 0x20, 0x00, 0x00, //FM_GPIO 1 Feature -9, 0x40, 0x03, 0x01, 0x00, 0x02, 0x00, 0x20, 0x00, 0x00, //FM_GPIO 2 Feature - -5, 0x20, 0x1E, 0x01, 0x00, 0x01 , -5, 0x20, 0x54, 0x01, 0x00, 0x00 , -7, 0x20, 0x0B, 0x01, 0x03, 0x7A, 0x00, 0x00 -//13, 0x30, 0x16, 0x01, 0x00, 0x21, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x11, 0x3A, //Dig_IO_IIS_SD_1 Mode -//5, 0x30, 0x0B, 0x01, 0x00, 0x00, //Mute -//5, 0x30, 0x0A, 0x01, 0xFF, 0xf0, //Volume -//7, 0x30, 0x0D, 0x01, 0x00, 0x80, 0x00, 0xE0,//Audio Output Source DAC L/R -}; - -typedef enum -{ - eAR_TuningAction_Standby = 0, - eAR_TuningAction_Preset = 1, /*!< Tune to new program with short mute time */ - eAR_TuningAction_Search = 2, /*!< Tune to new program and stay muted */ - eAR_TuningAction_AF_Update = 3, /*!< Tune to alternative frequency, store quality and tune back with inaudible mute */ - eAR_TuningAction_Jump = 4, /*!< Tune to alternative frequency with short inaudible mute */ - eAR_TuningAction_Check = 5, /*!< Tune to alternative frequency and stay muted */ - eAR_TuningAction_End = 7 /*!< Release the mute of a Search/Check action (frequency is ignored) */ -} AR_TuningAction_t, *pAR_TuningAction_t; -#endif |