diff options
Diffstat (limited to 'binding/radio_impl_rtlsdr.c')
-rw-r--r-- | binding/radio_impl_rtlsdr.c | 254 |
1 files changed, 254 insertions, 0 deletions
diff --git a/binding/radio_impl_rtlsdr.c b/binding/radio_impl_rtlsdr.c new file mode 100644 index 0000000..4364fd5 --- /dev/null +++ b/binding/radio_impl_rtlsdr.c @@ -0,0 +1,254 @@ +/* + * Copyright (C) 2017 Konsulko Group + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <stdbool.h> +#include <string.h> +#include <glib.h> + +#include "radio_impl.h" +#include "radio_output.h" +#include "rtl_fm.h" + +// Structure to describe FM band plans, all values in Hz. +typedef struct { + char *name; + uint32_t min; + uint32_t max; + uint32_t step; +} fm_band_plan_t; + +static fm_band_plan_t known_fm_band_plans[5] = { + { .name = "US", .min = 87900000, .max = 107900000, .step = 200000 }, + { .name = "JP", .min = 76100000, .max = 89900000, .step = 100000 }, + { .name = "EU", .min = 87500000, .max = 108000000, .step = 50000 }, + { .name = "ITU-1", .min = 87500000, .max = 108000000, .step = 50000 }, + { .name = "ITU-2", .min = 87900000, .max = 107900000, .step = 50000 } +}; + +static unsigned int bandplan; +static bool present; +static bool active; +static uint32_t current_frequency; + +static void rtl_output_callback(int16_t *result, int result_len, void *ctx) +{ + if(active) + radio_output_write((char*) result, result_len * 2); +} + +int radio_impl_init(void) +{ + GKeyFile* conf_file; + int conf_file_present = 0; + char *value_str; + + if(present) + return -1; + + // Load settings from configuration file if it exists + conf_file = g_key_file_new(); + if(conf_file && + g_key_file_load_from_dirs(conf_file, + "AGL.conf", + (const gchar**) g_get_system_config_dirs(), + NULL, + G_KEY_FILE_KEEP_COMMENTS, + NULL) == TRUE) { + conf_file_present = 1; + + // Set band plan if it is specified + value_str = g_key_file_get_string(conf_file, + "radio", + "fmbandplan", + NULL); + if(value_str) { + unsigned int i; + for(i = 0; + i < sizeof(known_fm_band_plans) / sizeof(fm_band_plan_t); + i++) { + if(!strcasecmp(value_str, known_fm_band_plans[i].name)) { + bandplan = i; + break; + } + } + } + } + fprintf(stderr, "Using FM Bandplan: %s\n", known_fm_band_plans[bandplan].name); + + current_frequency = radio_impl_get_min_frequency(BAND_FM); + if(rtl_fm_init(current_frequency, 200000, 48000, rtl_output_callback, NULL) < 0) { + return -1; + } + + if(conf_file_present) { + GError *error = NULL; + int n; + + // Allow over-riding scanning parameters just in case a demo + // setup needs to do so to work reliably. + n = g_key_file_get_integer(conf_file, + "radio", + "scan_squelch_level", + &error); + //error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND && + //error->code != G_KEY_FILE_ERROR_INVALID_VALUE) { + if(!error) { + fprintf(stderr, "Scanning squelch level set to %d\n", n); + rtl_fm_scan_set_squelch_level(n); + } + + error = NULL; + n = g_key_file_get_integer(conf_file, + "radio", + "scan_squelch_limit", + &error); + if(!error) { + fprintf(stderr, "Scanning squelch limit set to %d\n", n); + rtl_fm_scan_set_squelch_limit(n); + } + + g_key_file_free(conf_file); + } + + present = true; + return 0; +} + +uint32_t radio_impl_get_frequency(void) +{ + return current_frequency; +} + +void radio_impl_set_frequency(uint32_t frequency) +{ + if(!present) + return; + + if(frequency < known_fm_band_plans[bandplan].min || + frequency > known_fm_band_plans[bandplan].max) + return; + + radio_impl_scan_stop(); + current_frequency = frequency; + rtl_fm_set_freq(frequency); +} + +void radio_impl_set_frequency_callback(radio_freq_callback_t callback, + void *data) +{ + rtl_fm_set_freq_callback(callback, data); +} + +radio_band_t radio_impl_get_band(void) +{ + return BAND_FM; +} + +void radio_impl_set_band(radio_band_t band) +{ + // We only support FM, so do nothing +} + +int radio_impl_band_supported(radio_band_t band) +{ + if(band == BAND_FM) + return 1; + return 0; +} + +uint32_t radio_impl_get_min_frequency(radio_band_t band) +{ + return known_fm_band_plans[bandplan].min; +} + +uint32_t radio_impl_get_max_frequency(radio_band_t band) +{ + return known_fm_band_plans[bandplan].max; +} + +uint32_t radio_impl_get_frequency_step(radio_band_t band) +{ + uint32_t ret = 0; + + switch (band) { + case BAND_AM: + ret = 1000; // 1 kHz + break; + case BAND_FM: + ret = known_fm_band_plans[bandplan].step; + break; + default: + break; + } + return ret; +} + +void radio_impl_start(void) +{ + if(!present) + return; + + if(!active) { + if(radio_output_start() != 0) + return; + + rtl_fm_start(); + active = true; + } +} + +void radio_impl_stop(void) +{ + if(!present) + return; + + if(active) { + active = false; + radio_output_stop(); + rtl_fm_stop(); + + } +} + +void radio_impl_scan_start(radio_scan_direction_t direction, + radio_scan_callback_t callback, + void *data) +{ + rtl_fm_scan_start(direction == SCAN_FORWARD ? 0 : 1, + callback, + data, + radio_impl_get_frequency_step(BAND_FM), + radio_impl_get_min_frequency(BAND_FM), + radio_impl_get_max_frequency(BAND_FM)); +} + +void radio_impl_scan_stop(void) +{ + rtl_fm_scan_stop(); +} + +radio_stereo_mode_t radio_impl_get_stereo_mode(void) +{ + return STEREO; +} + +void radio_impl_set_stereo_mode(radio_stereo_mode_t mode) +{ + // We only support stereo, so do nothing +} |