summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md77
-rw-r--r--binding/CMakeLists.txt9
-rw-r--r--binding/binding.pro11
-rw-r--r--binding/radio-binding.c49
-rw-r--r--binding/radio_impl.h37
-rw-r--r--binding/radio_impl_kingfisher.c396
-rw-r--r--binding/radio_impl_kingfisher.h25
-rw-r--r--binding/radio_impl_rtlsdr.c75
-rw-r--r--binding/radio_impl_rtlsdr.h25
9 files changed, 631 insertions, 73 deletions
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..ae7e10b
--- /dev/null
+++ b/README.md
@@ -0,0 +1,77 @@
+# AGL Radio Tuner Binding
+
+## FM Band Plan Selection
+
+The FM band plan may be selected by adding:
+```
+fmbandplan=X
+```
+to the [radio] section in /etc/xdg/AGL.conf, where X is one of the
+following strings:
+
+US = United States / Canada
+JP = Japan
+EU = European Union
+ITU-1
+ITU-2
+
+Example:
+```
+[radio]
+fmbandplan=JP
+```
+
+## Implementation Specific Confguration
+
+### USB RTL-SDR adapter
+
+The scanning sensitivity can be tweaked by adding:
+```
+scan_squelch_level=X
+```
+to the [radio] section in /etc/xdg/AGL.conf, where X is an integer. Lower
+values make the scanning more sensitive. Default value is 140.
+
+Example:
+```
+[radio]
+scan_squelch_level=70
+```
+
+### M3ULCB Kingfisher Si4689
+
+The scanning sensitivity can be tweaked by adding:
+```
+scan_valid_snr_threshold=X
+scan_valid_rssi_threshold=Y
+```
+to the [radio] section in /etc/xdg/AGL.conf, where X and Y are integers
+between -127 and 127. The SNR value is in units of dB, and the RSSI is in
+units of dBuV. Lower values make the scanning more sensitive. Default
+values in the Si4689 are 10 and 17, respectively. You may determine the
+values that the Si4689 is seeing when tuning by examining the results of
+tuning in the systemd journal, looking for lines like:
+
+Example:
+```
+[radio]
+scan_valid_snr_threshold=7
+scan_valid_rssi_threshold=10
+```
+
+## Known Issues
+
+### M3ULCB Kingfisher
+
+Initial setup for a new Kingfisher board requires booting an image with
+Kingfisher support and running the commands:
+```
+si_init
+si_firmware_update
+```
+This installs the provided firmware into the flash attached to the Si4689.
+
+Since all operations are currently done by calling a patched version of
+Cogent Embedded's si_ctl utility, scanning currently cannot be interrupted.
+Additionally, sometimes a failure in scanning seems to result in muted
+state that currently has not been debugged.
diff --git a/binding/CMakeLists.txt b/binding/CMakeLists.txt
index 8f339e4..d0d23ed 100644
--- a/binding/CMakeLists.txt
+++ b/binding/CMakeLists.txt
@@ -21,12 +21,18 @@
PROJECT_TARGET_ADD(radio-binding)
# Define project Targets
- add_library(${TARGET_NAME} MODULE
+ set(radio_SOURCES
radio-binding.c
radio_output.c
radio_impl_rtlsdr.c
rtl_fm.c
convenience/convenience.c)
+ if(HAVE_KINGFISHER)
+ set(radio_SOURCES ${radio_SOURCES} radio_impl_kingfisher.c)
+ add_definitions(-DHAVE_KINGFISHER)
+ endif()
+
+ add_library(${TARGET_NAME} MODULE ${radio_SOURCES})
# Binder exposes a unique public entry point
SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES
@@ -42,4 +48,3 @@ PROJECT_TARGET_ADD(radio-binding)
# installation directory
INSTALL(TARGETS ${TARGET_NAME}
LIBRARY DESTINATION ${BINDINGS_INSTALL_DIR})
-
diff --git a/binding/binding.pro b/binding/binding.pro
deleted file mode 100644
index d8c5a93..0000000
--- a/binding/binding.pro
+++ /dev/null
@@ -1,11 +0,0 @@
-TARGET = radio-binding
-
-HEADERS = radio_impl.h radio_output.h rtl_fm.h convenience/convenience.h
-SOURCES = radio-binding.c radio_output.c radio_impl_rtlsdr.c rtl_fm.c convenience/convenience.c
-
-LIBS += -Wl,--version-script=$$PWD/export.map
-
-CONFIG += link_pkgconfig
-PKGCONFIG += json-c afb-daemon librtlsdr glib-2.0 libpulse-simple
-
-include(binding.pri)
diff --git a/binding/radio-binding.c b/binding/radio-binding.c
index f9b7e34..63c2491 100644
--- a/binding/radio-binding.c
+++ b/binding/radio-binding.c
@@ -29,6 +29,12 @@
#include <afb/afb-service-itf.h>
#include "radio_impl.h"
+#include "radio_impl_rtlsdr.h"
+#ifdef HAVE_KINGFISHER
+#include "radio_impl_kingfisher.h"
+#endif
+
+static radio_impl_ops_t *radio_impl_ops;
static struct afb_event freq_event;
static struct afb_event scan_event;
@@ -71,14 +77,14 @@ static void frequency(struct afb_req request)
char *p;
frequency = strtoul(value, &p, 10);
if(frequency && *p == '\0') {
- radio_impl_set_frequency(frequency);
+ radio_impl_ops->set_frequency(frequency);
} else {
afb_req_fail(request, "failed", "Invalid scan direction");
return;
}
}
ret_json = json_object_new_object();
- frequency = radio_impl_get_frequency();
+ frequency = radio_impl_ops->get_frequency();
json_object_object_add(ret_json, "frequency", json_object_new_int((int32_t) frequency));
afb_req_success(request, ret_json, NULL);
}
@@ -119,14 +125,14 @@ static void band(struct afb_req request)
}
}
if(valid) {
- radio_impl_set_band(band);
+ radio_impl_ops->set_band(band);
} else {
afb_req_fail(request, "failed", "Invalid band");
return;
}
}
ret_json = json_object_new_object();
- band = radio_impl_get_band();
+ 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_success(request, ret_json, NULL);
@@ -174,7 +180,7 @@ static void band_supported(struct afb_req request)
ret_json = json_object_new_object();
json_object_object_add(ret_json,
"supported",
- json_object_new_int(radio_impl_band_supported(band)));
+ json_object_new_int(radio_impl_ops->band_supported(band)));
afb_req_success(request, ret_json, NULL);
}
@@ -220,8 +226,8 @@ static void frequency_range(struct afb_req request)
return;
}
ret_json = json_object_new_object();
- min_frequency = radio_impl_get_min_frequency(band);
- max_frequency = radio_impl_get_max_frequency(band);
+ 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_success(request, ret_json, NULL);
@@ -268,7 +274,7 @@ static void frequency_step(struct afb_req request)
return;
}
ret_json = json_object_new_object();
- step = radio_impl_get_frequency_step(band);
+ step = radio_impl_ops->get_frequency_step(band);
json_object_object_add(ret_json, "step", json_object_new_int((int32_t) step));
afb_req_success(request, ret_json, NULL);
}
@@ -281,7 +287,7 @@ static void frequency_step(struct afb_req request)
*/
static void start(struct afb_req request)
{
- radio_impl_start();
+ radio_impl_ops->start();
afb_req_success(request, NULL, NULL);
}
@@ -293,7 +299,7 @@ static void start(struct afb_req request)
*/
static void stop(struct afb_req request)
{
- radio_impl_stop();
+ radio_impl_ops->stop();
afb_req_success(request, NULL, NULL);
}
@@ -335,7 +341,7 @@ static void scan_start(struct afb_req request)
afb_req_fail(request, "failed", "Invalid direction");
return;
}
- radio_impl_scan_start(direction, scan_callback, NULL);
+ radio_impl_ops->scan_start(direction, scan_callback, NULL);
afb_req_success(request, NULL, NULL);
}
@@ -347,7 +353,7 @@ static void scan_start(struct afb_req request)
*/
static void scan_stop(struct afb_req request)
{
- radio_impl_scan_stop();
+ radio_impl_ops->scan_stop();
afb_req_success(request, NULL, NULL);
}
@@ -387,14 +393,14 @@ static void stereo_mode(struct afb_req request)
}
}
if(valid) {
- radio_impl_set_stereo_mode(mode);
+ radio_impl_ops->set_stereo_mode(mode);
} else {
afb_req_fail(request, "failed", "Invalid mode");
return;
}
}
ret_json = json_object_new_object();
- mode = radio_impl_get_stereo_mode();
+ mode = radio_impl_ops->get_stereo_mode();
sprintf(mode_name, "%s", mode == MONO ? "mono" : "stereo");
json_object_object_add(ret_json, "mode", json_object_new_string(mode_name));
afb_req_success(request, ret_json, NULL);
@@ -467,11 +473,20 @@ static int init()
freq_event = afb_daemon_make_event("frequency");
scan_event = afb_daemon_make_event("station_found");
- rc = radio_impl_init();
+ // Look for RTL-SDR USB adapter
+ radio_impl_ops = &rtlsdr_impl_ops;
+ rc = radio_impl_ops->init();
+#ifdef HAVE_KINGFISHER
+ if(rc != 0) {
+ // Look for Kingfisher Si4689
+ radio_impl_ops = &kf_impl_ops;
+ rc = radio_impl_ops->init();
+ }
+#endif
if(rc == 0) {
- radio_impl_set_frequency_callback(freq_callback, NULL);
+ printf("%s found\n", radio_impl_ops->name);
+ radio_impl_ops->set_frequency_callback(freq_callback, NULL);
}
-
return rc;
}
diff --git a/binding/radio_impl.h b/binding/radio_impl.h
index 79e91a4..4b4a2f7 100644
--- a/binding/radio_impl.h
+++ b/binding/radio_impl.h
@@ -38,39 +38,44 @@ typedef enum {
STEREO
} radio_stereo_mode_t;
-int radio_impl_init(void);
+typedef struct {
+ char *name;
-uint32_t radio_impl_get_frequency(void);
+ int (*init)(void);
-void radio_impl_set_frequency(uint32_t frequency);
+ uint32_t (*get_frequency)(void);
-void radio_impl_set_frequency_callback(radio_freq_callback_t callback,
+ void (*set_frequency)(uint32_t frequency);
+
+ void (*set_frequency_callback)(radio_freq_callback_t callback,
void *data);
-radio_band_t radio_impl_get_band(void);
+ radio_band_t (*get_band)(void);
-void radio_impl_set_band(radio_band_t band);
+ void (*set_band)(radio_band_t band);
-int radio_impl_band_supported(radio_band_t band);
+ int (*band_supported)(radio_band_t band);
-uint32_t radio_impl_get_min_frequency(radio_band_t band);
+ uint32_t (*get_min_frequency)(radio_band_t band);
-uint32_t radio_impl_get_max_frequency(radio_band_t band);
+ uint32_t (*get_max_frequency)(radio_band_t band);
-uint32_t radio_impl_get_frequency_step(radio_band_t band);
+ uint32_t (*get_frequency_step)(radio_band_t band);
-void radio_impl_start(void);
+ void (*start)(void);
-void radio_impl_stop(void);
+ void (*stop)(void);
-void radio_impl_scan_start(radio_scan_direction_t direction,
+ void (*scan_start)(radio_scan_direction_t direction,
radio_scan_callback_t callback,
void *data);
-void radio_impl_scan_stop(void);
+ void (*scan_stop)(void);
+
+ radio_stereo_mode_t (*get_stereo_mode)(void);
-radio_stereo_mode_t radio_impl_get_stereo_mode(void);
+ void (*set_stereo_mode)(radio_stereo_mode_t mode);
-void radio_impl_set_stereo_mode(radio_stereo_mode_t mode);
+} radio_impl_ops_t;
#endif /* _RADIO_IMPL_H */
diff --git a/binding/radio_impl_kingfisher.c b/binding/radio_impl_kingfisher.c
new file mode 100644
index 0000000..069ca10
--- /dev/null
+++ b/binding/radio_impl_kingfisher.c
@@ -0,0 +1,396 @@
+/*
+ * Copyright (C) 2017 Konsulko Group
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <glib.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#include "radio_impl.h"
+
+#define SI_INIT "/usr/bin/si_init"
+#define SI_CTL "/usr/bin/si_ctl"
+
+// 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 present;
+static bool active;
+static uint32_t current_frequency;
+static int scan_valid_snr_threshold = 128;
+static int scan_valid_rssi_threshold = 128;
+static bool scanning = false;
+
+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 int kf_init(void)
+{
+ GKeyFile* conf_file;
+ int conf_file_present = 0;
+ char *value_str;
+ char cmd[128];
+ int rc;
+ char *output_sink;
+ struct stat statbuf;
+
+ if(present)
+ return -1;
+
+ // Check for Cogent's si_init script and si_ctl utility
+ if(stat(SI_INIT, &statbuf) != 0)
+ return -1;
+ if(stat(SI_CTL, &statbuf) != 0)
+ return -1;
+
+ // Load settings from configuration file if it exists
+ conf_file = g_key_file_new();
+ if(conf_file &&
+ g_key_file_load_from_dirs(conf_file,
+ "AGL.conf",
+ (const gchar**) g_get_system_config_dirs(),
+ NULL,
+ G_KEY_FILE_KEEP_COMMENTS,
+ NULL) == TRUE) {
+ conf_file_present = 1;
+
+ // Set band plan if it is specified
+ value_str = g_key_file_get_string(conf_file,
+ "radio",
+ "fmbandplan",
+ NULL);
+ if(value_str) {
+ unsigned int i;
+ for(i = 0;
+ i < sizeof(known_fm_band_plans) / sizeof(fm_band_plan_t);
+ i++) {
+ if(!strcasecmp(value_str, known_fm_band_plans[i].name)) {
+ bandplan = i;
+ break;
+ }
+ }
+ }
+ }
+
+ 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) {
+ fprintf(stderr, "Scan valid SNR level set to %d\n", n);
+ scan_valid_snr_threshold = n;
+ }
+
+ error = NULL;
+ n = g_key_file_get_integer(conf_file,
+ "radio",
+ "scan_valid_rssi_threshold",
+ &error);
+ if(!error) {
+ fprintf(stderr, "Scan valid SNR level set to %d\n", n);
+ scan_valid_rssi_threshold = n;
+ }
+
+ g_key_file_free(conf_file);
+ }
+
+ rc = system(SI_INIT);
+ if(rc != 0) {
+ fprintf(stderr, "si_init failed, rc = %d", rc);
+ return -1;
+ }
+
+ fprintf(stderr, "Using FM Bandplan: %s\n", known_fm_band_plans[bandplan].name);
+ current_frequency = kf_get_min_frequency(BAND_FM);
+ sprintf(cmd,
+ "%s /dev/i2c-11 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) {
+ fprintf(stderr, "%s failed, rc = %d", SI_CTL, rc);
+ return -1;
+ }
+
+ // Handle 4A disabling PA udev module
+ if(system("pactl list sources short |grep -q alsa_input.radio") != 0) {
+ // Set up radio source
+ if(system("pactl load-module module-alsa-source device=hw:radio,0 name=radio") != 0) {
+ fprintf(stderr, "radio PA source creation failed!\n");
+ return -1;
+ }
+ }
+
+ // Set initial state to muted
+ rc = system("pactl set-source-mute alsa_input.radio 1");
+ if(rc != 0) {
+ fprintf(stderr, "pactl failed, rc = %d", rc);
+ return -1;
+ }
+
+ // Set up loopback to output sink
+ output_sink = getenv("PULSE_SINK");
+ if(!output_sink) {
+ // On non-4A, loopback to the default output sink
+ output_sink = "0";
+ }
+ sprintf(cmd, "pactl load-module module-loopback source=alsa_input.radio sink=%s", output_sink);
+ rc = system(cmd);
+ if(rc != 0) {
+ fprintf(stderr, "pactl failed, rc = %d", rc);
+ return -1;
+ }
+
+ present = true;
+ return 0;
+}
+
+static uint32_t kf_get_frequency(void)
+{
+ return current_frequency;
+}
+
+static void kf_set_frequency(uint32_t frequency)
+{
+ char cmd[128];
+ int rc;
+
+ if(!present)
+ return;
+
+ if(scanning)
+ return;
+
+ if(frequency < known_fm_band_plans[bandplan].min ||
+ frequency > known_fm_band_plans[bandplan].max)
+ return;
+
+ kf_scan_stop();
+ sprintf(cmd, "%s /dev/i2c-11 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 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 void kf_start(void)
+{
+ int rc;
+
+ if(!present)
+ return;
+
+ if(!active) {
+ rc = system("pactl set-source-mute alsa_input.radio 0");
+ if(rc != 0)
+ fprintf(stderr, "pactl set-source-mute failed!\n");
+ active = true;
+ }
+}
+
+static void kf_stop(void)
+{
+ int rc;
+
+ if(!present)
+ return;
+
+ if(active) {
+ active = false;
+ rc = system("pactl set-source-mute alsa_input.radio 1");
+ if(rc != 0)
+ fprintf(stderr, "pactl set-source-mute failed!\n");
+ }
+}
+
+static void kf_scan_start(radio_scan_direction_t direction,
+ radio_scan_callback_t callback,
+ void *data)
+{
+ int rc;
+ char cmd[128];
+ char line[128];
+ uint32_t new_frequency = 0;
+ FILE *fp;
+
+ if(!present)
+ return;
+
+ if(scanning)
+ return;
+
+ scanning = true;
+ sprintf(cmd, "%s /dev/i2c-11 0x65 -l %s", SI_CTL, direction == SCAN_FORWARD ? "up" : "down");
+ fp = popen(cmd, "r");
+ if(fp == NULL) {
+ fprintf(stderr, "Could not run: %s!\n", cmd);
+ return;
+ }
+ // Look for "Frequency:" in output
+ while(fgets(line, 128, fp) != NULL) {
+ if(strncmp("Frequency:", line, 10) == 0) {
+ new_frequency = atoi(line + 10);
+ //fprintf(stderr, "%s: got new_frequency = %d\n", __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",
+ .init = kf_init,
+ .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,
+ .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
+};
diff --git a/binding/radio_impl_kingfisher.h b/binding/radio_impl_kingfisher.h
new file mode 100644
index 0000000..5d4f064
--- /dev/null
+++ b/binding/radio_impl_kingfisher.h
@@ -0,0 +1,25 @@
+/*
+ * 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_rtlsdr.c b/binding/radio_impl_rtlsdr.c
index 4364fd5..22d627e 100644
--- a/binding/radio_impl_rtlsdr.c
+++ b/binding/radio_impl_rtlsdr.c
@@ -35,7 +35,7 @@ typedef struct {
static fm_band_plan_t known_fm_band_plans[5] = {
{ .name = "US", .min = 87900000, .max = 107900000, .step = 200000 },
- { .name = "JP", .min = 76100000, .max = 89900000, .step = 100000 },
+ { .name = "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 }
@@ -46,13 +46,16 @@ static bool present;
static bool active;
static uint32_t current_frequency;
+static uint32_t rtlsdr_get_min_frequency(radio_band_t band);
+static void rtlsdr_scan_stop(void);
+
static void rtl_output_callback(int16_t *result, int result_len, void *ctx)
{
if(active)
radio_output_write((char*) result, result_len * 2);
}
-int radio_impl_init(void)
+static int rtlsdr_init(void)
{
GKeyFile* conf_file;
int conf_file_present = 0;
@@ -91,7 +94,7 @@ int radio_impl_init(void)
}
fprintf(stderr, "Using FM Bandplan: %s\n", known_fm_band_plans[bandplan].name);
- current_frequency = radio_impl_get_min_frequency(BAND_FM);
+ current_frequency = rtlsdr_get_min_frequency(BAND_FM);
if(rtl_fm_init(current_frequency, 200000, 48000, rtl_output_callback, NULL) < 0) {
return -1;
}
@@ -106,8 +109,6 @@ int radio_impl_init(void)
"radio",
"scan_squelch_level",
&error);
- //error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND &&
- //error->code != G_KEY_FILE_ERROR_INVALID_VALUE) {
if(!error) {
fprintf(stderr, "Scanning squelch level set to %d\n", n);
rtl_fm_scan_set_squelch_level(n);
@@ -130,12 +131,12 @@ int radio_impl_init(void)
return 0;
}
-uint32_t radio_impl_get_frequency(void)
+static uint32_t rtlsdr_get_frequency(void)
{
return current_frequency;
}
-void radio_impl_set_frequency(uint32_t frequency)
+static void rtlsdr_set_frequency(uint32_t frequency)
{
if(!present)
return;
@@ -144,45 +145,45 @@ void radio_impl_set_frequency(uint32_t frequency)
frequency > known_fm_band_plans[bandplan].max)
return;
- radio_impl_scan_stop();
+ rtlsdr_scan_stop();
current_frequency = frequency;
rtl_fm_set_freq(frequency);
}
-void radio_impl_set_frequency_callback(radio_freq_callback_t callback,
- void *data)
+static void rtlsdr_set_frequency_callback(radio_freq_callback_t callback,
+ void *data)
{
rtl_fm_set_freq_callback(callback, data);
}
-radio_band_t radio_impl_get_band(void)
+static radio_band_t rtlsdr_get_band(void)
{
return BAND_FM;
}
-void radio_impl_set_band(radio_band_t band)
+static void rtlsdr_set_band(radio_band_t band)
{
// We only support FM, so do nothing
}
-int radio_impl_band_supported(radio_band_t band)
+static int rtlsdr_band_supported(radio_band_t band)
{
if(band == BAND_FM)
return 1;
return 0;
}
-uint32_t radio_impl_get_min_frequency(radio_band_t band)
+static uint32_t rtlsdr_get_min_frequency(radio_band_t band)
{
return known_fm_band_plans[bandplan].min;
}
-uint32_t radio_impl_get_max_frequency(radio_band_t band)
+static uint32_t rtlsdr_get_max_frequency(radio_band_t band)
{
return known_fm_band_plans[bandplan].max;
}
-uint32_t radio_impl_get_frequency_step(radio_band_t band)
+static uint32_t rtlsdr_get_frequency_step(radio_band_t band)
{
uint32_t ret = 0;
@@ -199,7 +200,7 @@ uint32_t radio_impl_get_frequency_step(radio_band_t band)
return ret;
}
-void radio_impl_start(void)
+static void rtlsdr_start(void)
{
if(!present)
return;
@@ -213,7 +214,7 @@ void radio_impl_start(void)
}
}
-void radio_impl_stop(void)
+static void rtlsdr_stop(void)
{
if(!present)
return;
@@ -226,29 +227,49 @@ void radio_impl_stop(void)
}
}
-void radio_impl_scan_start(radio_scan_direction_t direction,
- radio_scan_callback_t callback,
- void *data)
+static void rtlsdr_scan_start(radio_scan_direction_t direction,
+ radio_scan_callback_t callback,
+ void *data)
{
rtl_fm_scan_start(direction == SCAN_FORWARD ? 0 : 1,
callback,
data,
- radio_impl_get_frequency_step(BAND_FM),
- radio_impl_get_min_frequency(BAND_FM),
- radio_impl_get_max_frequency(BAND_FM));
+ rtlsdr_get_frequency_step(BAND_FM),
+ rtlsdr_get_min_frequency(BAND_FM),
+ rtlsdr_get_max_frequency(BAND_FM));
}
-void radio_impl_scan_stop(void)
+static void rtlsdr_scan_stop(void)
{
rtl_fm_scan_stop();
}
-radio_stereo_mode_t radio_impl_get_stereo_mode(void)
+static radio_stereo_mode_t rtlsdr_get_stereo_mode(void)
{
return STEREO;
}
-void radio_impl_set_stereo_mode(radio_stereo_mode_t mode)
+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",
+ .init = rtlsdr_init,
+ .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
new file mode 100644
index 0000000..6c83338
--- /dev/null
+++ b/binding/radio_impl_rtlsdr.h
@@ -0,0 +1,25 @@
+/*
+ * 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 */
+