1
|
#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
|
}
|