Add sound manager initial source code
[staging/soundmanager.git] / sample / radio / binding / radio_impl_rtlsdr.c
1 /*
2  * Copyright (C) 2017 Konsulko Group
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <stdint.h>
20 #include <stdbool.h>
21 #include <string.h>
22 #include <glib.h>
23
24 #include "radio_impl.h"
25 #include "radio_output.h"
26 #include "rtl_fm.h"
27
28 // Structure to describe FM band plans, all values in Hz.
29 typedef struct {
30         char *name;
31         uint32_t min;
32         uint32_t max;
33         uint32_t step;
34 } fm_band_plan_t;
35
36 static fm_band_plan_t known_fm_band_plans[5] = {
37         { .name = "US", .min = 87900000, .max = 107900000, .step = 200000 },
38         { .name = "JP", .min = 76100000, .max = 89900000, .step = 100000 },
39         { .name = "EU", .min = 87500000, .max = 108000000, .step = 50000 },
40         { .name = "ITU-1", .min = 87500000, .max = 108000000, .step = 50000 },
41         { .name = "ITU-2", .min = 87900000, .max = 107900000, .step = 50000 }
42 };
43
44 static unsigned int bandplan;
45 static bool present;
46 static bool active;
47 static uint32_t current_frequency;
48
49 static void rtl_output_callback(int16_t *result, int result_len, void *ctx)
50 {
51     if(active)
52             radio_output_write((char*) result, result_len * 2);
53 }
54
55 int radio_impl_init(void)
56 {
57         GKeyFile* conf_file;
58         int conf_file_present = 0;
59         char *value_str;
60
61         if(present)
62                 return -1;
63
64         // Load settings from configuration file if it exists
65         conf_file = g_key_file_new();
66         if(conf_file &&
67            g_key_file_load_from_dirs(conf_file,
68                                      "AGL.conf",
69                                      (const gchar**) g_get_system_config_dirs(),
70                                      NULL,
71                                      G_KEY_FILE_KEEP_COMMENTS,
72                                      NULL) == TRUE) {
73                 conf_file_present = 1;
74
75                 // Set band plan if it is specified
76                 value_str = g_key_file_get_string(conf_file,
77                                                   "radio",
78                                                   "fmbandplan",
79                                                   NULL);
80                 if(value_str) {
81                         unsigned int i;
82                         for(i = 0;
83                             i < sizeof(known_fm_band_plans) / sizeof(fm_band_plan_t);
84                             i++) {
85                                 if(!strcasecmp(value_str, known_fm_band_plans[i].name)) {
86                                         bandplan = i;
87                                         break;
88                                 }
89                         }
90                 }
91         }
92         fprintf(stderr, "Using FM Bandplan: %s\n", known_fm_band_plans[bandplan].name);
93
94         current_frequency = radio_impl_get_min_frequency(BAND_FM);
95         if(rtl_fm_init(current_frequency, 200000, 48000, rtl_output_callback, NULL) < 0) {
96                 return -1;
97         }
98
99         if(conf_file_present) {
100                 GError *error = NULL;
101                 int n;
102
103                 // Allow over-riding scanning parameters just in case a demo
104                 // setup needs to do so to work reliably.
105                 n = g_key_file_get_integer(conf_file,
106                                            "radio",
107                                            "scan_squelch_level",
108                                            &error);
109                 //error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND &&
110                 //error->code != G_KEY_FILE_ERROR_INVALID_VALUE) {
111                 if(!error) {
112                         fprintf(stderr, "Scanning squelch level set to %d\n", n);
113                         rtl_fm_scan_set_squelch_level(n);
114                 }
115
116                 error = NULL;
117                 n = g_key_file_get_integer(conf_file,
118                                            "radio",
119                                            "scan_squelch_limit",
120                                            &error);
121                 if(!error) {
122                         fprintf(stderr, "Scanning squelch limit set to %d\n", n);
123                         rtl_fm_scan_set_squelch_limit(n);
124                 }
125
126                 g_key_file_free(conf_file);
127         }
128
129         present = true;
130         return 0;
131 }
132
133 uint32_t radio_impl_get_frequency(void)
134 {
135         return current_frequency;
136 }
137
138 void radio_impl_set_frequency(uint32_t frequency)
139 {
140         if(!present)
141                 return;
142
143         if(frequency < known_fm_band_plans[bandplan].min ||
144            frequency > known_fm_band_plans[bandplan].max)
145                 return;
146
147         radio_impl_scan_stop();
148         current_frequency = frequency;
149         rtl_fm_set_freq(frequency);
150 }
151
152 void radio_impl_set_frequency_callback(radio_freq_callback_t callback,
153                                        void *data)
154 {
155         rtl_fm_set_freq_callback(callback, data);
156 }
157
158 radio_band_t radio_impl_get_band(void)
159 {
160         return BAND_FM;
161 }
162
163 void radio_impl_set_band(radio_band_t band)
164 {
165         // We only support FM, so do nothing
166 }
167
168 int radio_impl_band_supported(radio_band_t band)
169 {
170         if(band == BAND_FM)
171                 return 1;
172         return 0;
173 }
174
175 uint32_t radio_impl_get_min_frequency(radio_band_t band)
176 {
177         return known_fm_band_plans[bandplan].min;
178 }
179
180 uint32_t radio_impl_get_max_frequency(radio_band_t band)
181 {
182         return known_fm_band_plans[bandplan].max;
183 }
184
185 uint32_t radio_impl_get_frequency_step(radio_band_t band)
186 {
187         uint32_t ret = 0;
188
189         switch (band) {
190         case BAND_AM:
191                 ret = 1000; // 1 kHz
192                 break;
193         case BAND_FM:
194                 ret = known_fm_band_plans[bandplan].step;
195                 break;
196         default:
197                 break;
198         }
199         return ret;
200 }
201
202 void radio_impl_start(void)
203 {
204         if(!present)
205                 return;
206
207         if(!active) {
208                 if(radio_output_start() != 0)
209                         return;
210
211                 rtl_fm_start();
212                 active = true;
213         }
214 }
215
216 void radio_impl_stop(void)
217 {
218         if(!present)
219                 return;
220
221         if(active) {
222                 active = false;
223                 radio_output_stop();
224                 rtl_fm_stop();
225
226         }
227 }
228
229 void radio_impl_scan_start(radio_scan_direction_t direction,
230                            radio_scan_callback_t callback,
231                            void *data)
232 {
233         rtl_fm_scan_start(direction == SCAN_FORWARD ? 0 : 1,
234                           callback,
235                           data,
236                           radio_impl_get_frequency_step(BAND_FM),
237                           radio_impl_get_min_frequency(BAND_FM),
238                           radio_impl_get_max_frequency(BAND_FM));
239 }
240
241 void radio_impl_scan_stop(void)
242 {
243         rtl_fm_scan_stop();
244 }
245
246 radio_stereo_mode_t radio_impl_get_stereo_mode(void)
247 {
248         return STEREO;
249 }
250
251 void radio_impl_set_stereo_mode(radio_stereo_mode_t mode)
252 {
253         // We only support stereo, so do nothing
254 }