1 |
956debaf
|
Christian Daniel
|
#include <stdio.h>
|
2 |
|
|
#include <stdlib.h>
|
3 |
|
|
#include <string.h>
|
4 |
|
|
#include <unistd.h>
|
5 |
|
|
#include <math.h>
|
6 |
|
|
#include <portaudio.h>
|
7 |
|
|
#include "serial.h"
|
8 |
|
|
#include "utils.h"
|
9 |
|
|
|
10 |
|
|
struct SweepState {
|
11 |
|
|
int serialFd;
|
12 |
|
|
int settleTime;
|
13 |
|
|
int sampleTime;
|
14 |
|
|
uint64_t freq;
|
15 |
|
|
double acc;
|
16 |
|
|
double num;
|
17 |
|
|
double start;
|
18 |
|
|
double stop;
|
19 |
|
|
};
|
20 |
|
|
|
21 |
|
|
static int printSyntax()
|
22 |
|
|
{
|
23 |
|
|
fprintf(stderr, "Error: Invalid command line!\n\n"
|
24 |
|
|
"syntax: sdr-sweep /dev/ttyUSB0 start stop"
|
25 |
|
|
" - start = start frequency in MHz, e.g 88.5\n"
|
26 |
|
|
" - stop = stop frequency in MHz, e.g. 108\n"
|
27 |
|
|
"frequency range of OsmoSDR is 64MHz - 1700MHz\n");
|
28 |
|
|
|
29 |
|
|
return EXIT_FAILURE;
|
30 |
|
|
}
|
31 |
|
|
|
32 |
|
|
static void setFreq(int fd, uint64_t freq)
|
33 |
|
|
{
|
34 |
|
|
char str[128];
|
35 |
|
|
char* c;
|
36 |
|
|
char str2[2] = { '\0', '\0' };
|
37 |
|
|
|
38 |
|
|
while(serialGetS(fd, str, sizeof(str), 1) >= 0)
|
39 |
|
|
;
|
40 |
|
|
|
41 |
|
|
sprintf(str, "tuner.freq=%llu\r", freq);
|
42 |
|
|
|
43 |
|
|
for(c = str; *c != '\0'; c++) {
|
44 |
|
|
serialPutC(fd, *c);
|
45 |
|
|
if(*c == '\r')
|
46 |
|
|
str2[0] = '\n';
|
47 |
|
|
else str2[0] = *c;
|
48 |
|
|
if(serialExpect(fd, str2, 1) < 0) {
|
49 |
|
|
serialPutC(fd, *c);
|
50 |
|
|
if(serialExpect(fd, str2, 1) < 0) {
|
51 |
|
|
fprintf(stderr, "serial port broken (%d = %c)\n", *c, *c);
|
52 |
|
|
return;
|
53 |
|
|
}
|
54 |
|
|
}
|
55 |
|
|
}
|
56 |
|
|
}
|
57 |
|
|
|
58 |
|
|
static int audioCallback(const void* inputBuffer, void* outputBuffer,
|
59 |
|
|
unsigned long framesPerBuffer,
|
60 |
|
|
const PaStreamCallbackTimeInfo* timeInfo,
|
61 |
|
|
PaStreamCallbackFlags statusFlags,
|
62 |
|
|
void* userData)
|
63 |
|
|
{
|
64 |
|
|
struct SweepState* state = (struct SweepState*)userData;
|
65 |
|
|
int i;
|
66 |
|
|
const int16_t* input = (const int16_t*)inputBuffer;
|
67 |
|
|
uint8_t c;
|
68 |
|
|
double v;
|
69 |
|
|
int max;
|
70 |
|
|
|
71 |
|
|
//fprintf(stderr, "block %lu\n", framesPerBuffer);
|
72 |
|
|
|
73 |
|
|
while(serialGetC(state->serialFd, &c, 0) >= 0)
|
74 |
|
|
//fprintf(stderr, "%c", c);
|
75 |
|
|
;
|
76 |
|
|
|
77 |
|
|
if(state->settleTime > 0) {
|
78 |
|
|
state->settleTime -= framesPerBuffer;
|
79 |
|
|
if(state->settleTime <= 0) {
|
80 |
|
|
state->sampleTime = 100000;
|
81 |
|
|
state->acc = 0.0;
|
82 |
|
|
state->num = 0.0;
|
83 |
|
|
}
|
84 |
|
|
return 0;
|
85 |
|
|
}
|
86 |
|
|
|
87 |
|
|
if(state->sampleTime > 0) {
|
88 |
|
|
max = 0;
|
89 |
|
|
for(i = 0; i < framesPerBuffer; i++) {
|
90 |
|
|
if(abs(*input) > max)
|
91 |
|
|
max = abs(*input);
|
92 |
|
|
v = *input / 32768.0;
|
93 |
|
|
state->acc += v * v;
|
94 |
|
|
input++;
|
95 |
|
|
if(abs(*input) > max)
|
96 |
|
|
max = abs(*input);
|
97 |
|
|
v = *input / 32768.0;
|
98 |
|
|
state->acc += v * v;
|
99 |
|
|
input++;
|
100 |
|
|
state->num += 2;
|
101 |
|
|
}
|
102 |
|
|
//fprintf(stderr, "-> %d\n", max);
|
103 |
|
|
|
104 |
|
|
state->sampleTime -= framesPerBuffer;
|
105 |
|
|
if(state->sampleTime <= 0) {
|
106 |
|
|
printf("%.1f\t%.1f\n", state->freq / 1000000.0, 20.0 * log10(sqrt(state->acc / state->num)));
|
107 |
|
|
fflush(stdout);
|
108 |
|
|
state->freq += 100000;
|
109 |
|
|
setFreq(state->serialFd, state->freq);
|
110 |
|
|
state->settleTime = 500000;
|
111 |
|
|
}
|
112 |
|
|
}
|
113 |
|
|
|
114 |
|
|
return 0;
|
115 |
|
|
}
|
116 |
|
|
|
117 |
|
|
static PaStream* audioOpen(struct SweepState* state)
|
118 |
|
|
{
|
119 |
|
|
int numDevices;
|
120 |
|
|
const PaDeviceInfo* deviceInfo;
|
121 |
|
|
PaError err;
|
122 |
|
|
int i;
|
123 |
|
|
int dev = -1;
|
124 |
|
|
PaStream* stream = NULL;
|
125 |
|
|
PaStreamParameters inputParameters;
|
126 |
|
|
|
127 |
|
|
err = Pa_Initialize();
|
128 |
|
|
if(err != paNoError)
|
129 |
|
|
goto error_noinit;
|
130 |
|
|
|
131 |
|
|
numDevices = Pa_GetDeviceCount();
|
132 |
|
|
if(numDevices < 0) {
|
133 |
|
|
err = numDevices;
|
134 |
|
|
goto error;
|
135 |
|
|
}
|
136 |
|
|
|
137 |
|
|
for(i = 0; i < numDevices; i++) {
|
138 |
|
|
deviceInfo = Pa_GetDeviceInfo(i);
|
139 |
|
|
fprintf(stderr, "#%02d -> [%s]\n", i, deviceInfo->name);
|
140 |
|
|
if(strncmp(deviceInfo->name, "OsmoSDR", 7) == 0)
|
141 |
|
|
dev = i;
|
142 |
|
|
}
|
143 |
|
|
if(dev < 0) {
|
144 |
|
|
fprintf(stderr, "OsmoSDR not found!\n");
|
145 |
|
|
Pa_Terminate();
|
146 |
|
|
return NULL;
|
147 |
|
|
}
|
148 |
|
|
|
149 |
|
|
fprintf(stderr, "Using device #%02d (sample rate %.0f Hz)\n", dev, Pa_GetDeviceInfo(dev)->defaultSampleRate);
|
150 |
|
|
|
151 |
|
|
bzero(&inputParameters, sizeof(inputParameters));
|
152 |
|
|
inputParameters.channelCount = 2;
|
153 |
|
|
inputParameters.device = dev;
|
154 |
|
|
inputParameters.hostApiSpecificStreamInfo = NULL;
|
155 |
|
|
inputParameters.sampleFormat = paInt16;
|
156 |
|
|
inputParameters.suggestedLatency = Pa_GetDeviceInfo(dev)->defaultHighInputLatency ;
|
157 |
|
|
inputParameters.hostApiSpecificStreamInfo = NULL;
|
158 |
|
|
|
159 |
|
|
err = Pa_OpenStream(&stream, &inputParameters, NULL, Pa_GetDeviceInfo(dev)->defaultSampleRate, 20000,
|
160 |
|
|
paClipOff | paDitherOff, audioCallback, state);
|
161 |
|
|
if(err != paNoError)
|
162 |
|
|
goto error_noinit;
|
163 |
|
|
|
164 |
|
|
err = Pa_StartStream(stream);
|
165 |
|
|
if(err != paNoError)
|
166 |
|
|
goto error_noinit;
|
167 |
|
|
|
168 |
|
|
return stream;
|
169 |
|
|
|
170 |
|
|
error:
|
171 |
|
|
Pa_Terminate();
|
172 |
|
|
|
173 |
|
|
error_noinit:
|
174 |
|
|
fprintf(stderr, "PortAudio error: %s\n", Pa_GetErrorText(err));
|
175 |
|
|
return NULL;
|
176 |
|
|
}
|
177 |
|
|
|
178 |
|
|
static void audioClose(PaStream* stream)
|
179 |
|
|
{
|
180 |
|
|
PaError err;
|
181 |
|
|
|
182 |
|
|
err = Pa_StopStream(stream);
|
183 |
|
|
if(err != paNoError)
|
184 |
|
|
fprintf(stderr, "PortAudio error: %s\n", Pa_GetErrorText(err));
|
185 |
|
|
|
186 |
|
|
err = Pa_CloseStream(stream);
|
187 |
|
|
if(err != paNoError)
|
188 |
|
|
fprintf(stderr, "PortAudio error: %s\n", Pa_GetErrorText(err));
|
189 |
|
|
|
190 |
|
|
err = Pa_Terminate();
|
191 |
|
|
if(err != paNoError)
|
192 |
|
|
fprintf(stderr, "PortAudio error: %s\n", Pa_GetErrorText(err));
|
193 |
|
|
}
|
194 |
|
|
|
195 |
|
|
int main(int argc, char* argv[])
|
196 |
|
|
{
|
197 |
|
|
HANDLE fd;
|
198 |
|
|
PaStream* stream;
|
199 |
|
|
struct SweepState state;
|
200 |
|
|
|
201 |
|
|
if(argc < 4)
|
202 |
|
|
return printSyntax();
|
203 |
|
|
|
204 |
|
|
if((fd = serialOpen(argv[1])) == INVALID_HANDLE_VALUE)
|
205 |
|
|
return EXIT_FAILURE;
|
206 |
|
|
|
207 |
|
|
memset(&state, 0x00, sizeof(struct SweepState));
|
208 |
|
|
state.serialFd = fd;
|
209 |
|
|
state.settleTime = 100000;
|
210 |
|
|
state.start = atof(argv[2]);
|
211 |
|
|
state.stop = atof(argv[3]);
|
212 |
|
|
state.freq = state.start * 1000000.0;
|
213 |
|
|
serialPutS(fd, "\r\r\rtuner.init!\r\r\r");
|
214 |
|
|
setFreq(fd, state.freq);
|
215 |
|
|
|
216 |
|
|
if((stream = audioOpen(&state)) == NULL) {
|
217 |
|
|
serialClose(fd);
|
218 |
|
|
return EXIT_FAILURE;
|
219 |
|
|
}
|
220 |
|
|
|
221 |
|
|
fprintf(stderr, "sweep running...\n");
|
222 |
|
|
|
223 |
|
|
while(1) {
|
224 |
|
|
sleep(1);
|
225 |
|
|
if(state.freq > state.stop * 1000000.0)
|
226 |
|
|
break;
|
227 |
|
|
fprintf(stderr, "...%.1f MHz...\n", state.freq / 1000000.0);
|
228 |
|
|
}
|
229 |
|
|
|
230 |
|
|
audioClose(stream);
|
231 |
|
|
serialClose(fd);
|
232 |
|
|
|
233 |
|
|
return EXIT_FAILURE;
|
234 |
|
|
}
|