2011-07-07 14:09:57 +00:00
|
|
|
|
#include <stdlib.h>
|
2011-08-20 11:49:29 +00:00
|
|
|
|
#include <stdbool.h>
|
2011-07-07 14:09:57 +00:00
|
|
|
|
#include <math.h>
|
|
|
|
|
#include <gtk/gtk.h>
|
2011-07-29 20:09:42 +00:00
|
|
|
|
#include <alsa/asoundlib.h>
|
2011-07-07 14:09:57 +00:00
|
|
|
|
|
2012-12-25 20:43:26 +00:00
|
|
|
|
#ifdef GPL
|
|
|
|
|
#include <fftw3.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
2011-07-07 14:09:57 +00:00
|
|
|
|
#include "common.h"
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
*
|
2011-08-30 16:21:03 +00:00
|
|
|
|
* Detect VIS & frequency shift
|
2011-07-07 14:09:57 +00:00
|
|
|
|
*
|
|
|
|
|
* Each bit lasts 30 ms (1323 samples)
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|
2011-08-11 11:48:35 +00:00
|
|
|
|
guchar GetVIS () {
|
|
|
|
|
|
2011-08-20 05:51:27 +00:00
|
|
|
|
int selmode, ptr=0;
|
2011-08-18 00:04:28 +00:00
|
|
|
|
//int Pointer = 0;
|
|
|
|
|
int VIS = 0, Parity = 0, HedrPtr = 0;
|
|
|
|
|
//gushort MaxPcm = 0;
|
2011-08-11 11:48:35 +00:00
|
|
|
|
guint FFTLen = 2048, i=0, j=0, k=0, MaxBin = 0;
|
2011-08-16 21:29:38 +00:00
|
|
|
|
double Power[2048] = {0}, HedrBuf[100] = {0}, tone[100] = {0}, Hann[882] = {0};
|
2011-08-11 11:48:35 +00:00
|
|
|
|
char infostr[60] = {0};
|
2011-08-20 11:49:29 +00:00
|
|
|
|
bool gotvis = false;
|
2011-08-18 00:04:28 +00:00
|
|
|
|
guchar Bit[8] = {0}, ParityBit = 0;
|
2011-07-07 14:09:57 +00:00
|
|
|
|
|
2011-08-18 00:04:28 +00:00
|
|
|
|
for (i = 0; i < FFTLen; i++) in[i] = 0;
|
2011-07-26 07:10:43 +00:00
|
|
|
|
|
2011-07-29 20:09:42 +00:00
|
|
|
|
// Create 20ms Hann window
|
2011-08-16 21:29:38 +00:00
|
|
|
|
for (i = 0; i < 882; i++) Hann[i] = 0.5 * (1 - cos( (2 * M_PI * (double)i) / 881 ) );
|
2011-07-07 14:09:57 +00:00
|
|
|
|
|
2011-08-20 11:49:29 +00:00
|
|
|
|
ManualActivated = false;
|
2011-08-11 11:48:35 +00:00
|
|
|
|
|
|
|
|
|
printf("Waiting for header\n");
|
|
|
|
|
|
|
|
|
|
gdk_threads_enter();
|
|
|
|
|
gtk_statusbar_push( GTK_STATUSBAR(statusbar), 0, "Ready" );
|
|
|
|
|
gdk_threads_leave();
|
2011-08-10 20:43:17 +00:00
|
|
|
|
|
2011-08-20 11:49:29 +00:00
|
|
|
|
while ( true ) {
|
2011-07-07 14:09:57 +00:00
|
|
|
|
|
2011-08-16 21:29:38 +00:00
|
|
|
|
// Read 10 ms from sound card
|
|
|
|
|
readPcm(441);
|
2011-07-07 14:09:57 +00:00
|
|
|
|
|
|
|
|
|
// Apply Hann window
|
2011-08-16 21:29:38 +00:00
|
|
|
|
for (i = 0; i < 882; i++) in[i] = PcmBuffer[PcmPointer + i - 441] / 32768.0 * Hann[i];
|
2011-07-07 14:09:57 +00:00
|
|
|
|
|
2011-07-25 22:41:04 +00:00
|
|
|
|
// FFT of last 20 ms
|
2011-08-16 21:29:38 +00:00
|
|
|
|
fftw_execute(Plan2048);
|
2011-07-07 14:09:57 +00:00
|
|
|
|
|
2011-08-20 05:51:27 +00:00
|
|
|
|
// Find the bin with most power
|
2011-08-18 00:04:28 +00:00
|
|
|
|
MaxBin = 0;
|
2011-07-25 12:05:03 +00:00
|
|
|
|
for (i = GetBin(500, FFTLen); i <= GetBin(3300, FFTLen); i++) {
|
2011-07-07 14:09:57 +00:00
|
|
|
|
Power[i] = pow(out[i], 2) + pow(out[FFTLen - i], 2);
|
|
|
|
|
if (Power[i] > Power[MaxBin] || MaxBin == 0) MaxBin = i;
|
|
|
|
|
}
|
|
|
|
|
|
2011-08-20 05:51:27 +00:00
|
|
|
|
// Find the peak frequency by Gaussian interpolation
|
2011-08-18 00:04:28 +00:00
|
|
|
|
if (MaxBin > GetBin(500, FFTLen) && MaxBin < GetBin(3300, FFTLen))
|
|
|
|
|
HedrBuf[HedrPtr] = MaxBin + (log( Power[MaxBin + 1] / Power[MaxBin - 1] )) /
|
|
|
|
|
(2 * log( pow(Power[MaxBin], 2) / (Power[MaxBin + 1] * Power[MaxBin - 1])));
|
|
|
|
|
else HedrBuf[HedrPtr] = HedrBuf[(HedrPtr-1) % 45];
|
2011-07-07 14:09:57 +00:00
|
|
|
|
|
2011-08-18 00:04:28 +00:00
|
|
|
|
// In Hertz
|
|
|
|
|
HedrBuf[HedrPtr] = HedrBuf[HedrPtr] / FFTLen * 44100;
|
2011-07-07 14:09:57 +00:00
|
|
|
|
|
2011-08-18 00:04:28 +00:00
|
|
|
|
// Header buffer holds 45 * 10 msec = 450 msec
|
|
|
|
|
HedrPtr = (HedrPtr + 1) % 45;
|
|
|
|
|
|
2011-08-20 05:51:27 +00:00
|
|
|
|
// Frequencies in the last 450 msec
|
2011-08-18 00:04:28 +00:00
|
|
|
|
for (i = 0; i < 45; i++) tone[i] = HedrBuf[(HedrPtr + i) % 45];
|
2011-07-07 14:09:57 +00:00
|
|
|
|
|
|
|
|
|
// Is there a pattern that looks like (the end of) a calibration header + VIS?
|
2011-08-20 05:51:27 +00:00
|
|
|
|
// Tolerance ±25 Hz
|
2011-07-07 14:09:57 +00:00
|
|
|
|
HedrShift = 0;
|
2011-08-20 11:49:29 +00:00
|
|
|
|
gotvis = false;
|
2011-07-07 14:09:57 +00:00
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
|
|
|
if (HedrShift != 0) break;
|
|
|
|
|
for (j = 0; j < 3; j++) {
|
2011-08-20 05:51:27 +00:00
|
|
|
|
if ( (tone[1*3+i] > tone[0+j] - 25 && tone[1*3+i] < tone[0+j] + 25) && // 1900 Hz leader
|
|
|
|
|
(tone[2*3+i] > tone[0+j] - 25 && tone[2*3+i] < tone[0+j] + 25) && // 1900 Hz leader
|
|
|
|
|
(tone[3*3+i] > tone[0+j] - 25 && tone[3*3+i] < tone[0+j] + 25) && // 1900 Hz leader
|
|
|
|
|
(tone[4*3+i] > tone[0+j] - 25 && tone[4*3+i] < tone[0+j] + 25) && // 1900 Hz leader
|
|
|
|
|
(tone[5*3+i] > tone[0+j] - 725 && tone[5*3+i] < tone[0+j] - 675) && // 1200 Hz start bit
|
2011-08-18 00:04:28 +00:00
|
|
|
|
// ...8 VIS bits...
|
2011-08-20 05:51:27 +00:00
|
|
|
|
(tone[14*3+i] > tone[0+j] - 725 && tone[14*3+i] < tone[0+j] - 675) // 1200 Hz stop bit
|
2011-07-07 14:09:57 +00:00
|
|
|
|
) {
|
|
|
|
|
|
|
|
|
|
printf("Possible header @ %+.0f Hz\n",tone[0+j]-1900);
|
|
|
|
|
|
2011-08-18 00:04:28 +00:00
|
|
|
|
// Attempt to read VIS
|
2011-07-07 14:09:57 +00:00
|
|
|
|
|
2011-08-20 11:49:29 +00:00
|
|
|
|
gotvis = true;
|
2011-07-07 14:09:57 +00:00
|
|
|
|
for (k = 0; k < 8; k++) {
|
2011-08-20 05:51:27 +00:00
|
|
|
|
if (tone[6*3+i+3*k] > tone[0+j] - 625 && tone[6*3+i+3*k] < tone[0+j] - 575) Bit[k] = 0;
|
|
|
|
|
else if (tone[6*3+i+3*k] > tone[0+j] - 825 && tone[6*3+i+3*k] < tone[0+j] - 775) Bit[k] = 1;
|
2011-07-07 14:09:57 +00:00
|
|
|
|
else { // erroneous bit
|
2011-08-20 11:49:29 +00:00
|
|
|
|
gotvis = false;
|
2011-07-07 14:09:57 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-08-10 20:43:17 +00:00
|
|
|
|
if (gotvis) {
|
2011-07-07 14:09:57 +00:00
|
|
|
|
HedrShift = tone[0+j] - 1900;
|
|
|
|
|
|
|
|
|
|
VIS = Bit[0] + (Bit[1] << 1) + (Bit[2] << 2) + (Bit[3] << 3) + (Bit[4] << 4) +
|
|
|
|
|
(Bit[5] << 5) + (Bit[6] << 6);
|
|
|
|
|
ParityBit = Bit[7];
|
|
|
|
|
|
2011-08-11 11:48:35 +00:00
|
|
|
|
printf(" VIS %d (%02Xh) @ %d Hz\n", VIS, VIS, HedrShift);
|
2011-07-07 14:09:57 +00:00
|
|
|
|
|
|
|
|
|
Parity = Bit[0] ^ Bit[1] ^ Bit[2] ^ Bit[3] ^ Bit[4] ^ Bit[5] ^ Bit[6];
|
|
|
|
|
|
2011-08-20 05:51:27 +00:00
|
|
|
|
if (VISmap[VIS] == R12BW) Parity = !Parity;
|
|
|
|
|
|
|
|
|
|
if (Parity != ParityBit) {
|
2011-07-07 14:09:57 +00:00
|
|
|
|
printf(" Parity fail\n");
|
2011-08-20 11:49:29 +00:00
|
|
|
|
gotvis = false;
|
2011-07-07 14:09:57 +00:00
|
|
|
|
} else if (VISmap[VIS] == UNKNOWN) {
|
|
|
|
|
printf(" Unknown VIS\n");
|
|
|
|
|
snprintf(infostr, sizeof(infostr)-1, "How to decode image with VIS %d (%02Xh)?", VIS, VIS);
|
2011-08-20 11:49:29 +00:00
|
|
|
|
gotvis = false;
|
2011-07-07 14:09:57 +00:00
|
|
|
|
gdk_threads_enter();
|
|
|
|
|
gtk_label_set_markup(GTK_LABEL(infolabel), infostr);
|
|
|
|
|
gdk_threads_leave();
|
|
|
|
|
} else {
|
2012-04-06 12:38:41 +00:00
|
|
|
|
gdk_threads_enter();
|
|
|
|
|
gtk_combo_box_set_active (GTK_COMBO_BOX(modecombo), VISmap[VIS]-1);
|
|
|
|
|
gtk_spin_button_set_value (GTK_SPIN_BUTTON(shiftspin), HedrShift);
|
|
|
|
|
gdk_threads_leave();
|
2011-07-07 14:09:57 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-08-18 00:04:28 +00:00
|
|
|
|
if (gotvis)
|
2011-08-16 21:29:38 +00:00
|
|
|
|
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(togrx))) break;
|
2011-08-10 20:43:17 +00:00
|
|
|
|
|
|
|
|
|
// Manual start
|
|
|
|
|
if (ManualActivated) {
|
|
|
|
|
|
|
|
|
|
gdk_threads_enter();
|
2011-08-20 11:49:29 +00:00
|
|
|
|
gtk_widget_set_sensitive( manualframe, false );
|
2011-08-10 20:43:17 +00:00
|
|
|
|
gdk_threads_leave();
|
|
|
|
|
|
|
|
|
|
selmode = gtk_combo_box_get_active (GTK_COMBO_BOX(modecombo)) + 1;
|
|
|
|
|
HedrShift = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON(shiftspin));
|
|
|
|
|
VIS = 0;
|
|
|
|
|
for (i=0; i<0x80; i++) {
|
|
|
|
|
if (VISmap[i] == selmode) {
|
|
|
|
|
VIS = i;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
2011-07-07 14:09:57 +00:00
|
|
|
|
|
2011-08-20 05:51:27 +00:00
|
|
|
|
if (++ptr == 25) {
|
|
|
|
|
setVU(MaxPcm, -20);
|
|
|
|
|
MaxPcm = 0;
|
|
|
|
|
ptr = 0;
|
|
|
|
|
}
|
2011-07-07 14:09:57 +00:00
|
|
|
|
|
2011-08-16 21:29:38 +00:00
|
|
|
|
PcmPointer += 441;
|
|
|
|
|
}
|
2011-07-07 14:09:57 +00:00
|
|
|
|
|
2011-08-20 05:51:27 +00:00
|
|
|
|
// Skip the rest of the stop bit
|
|
|
|
|
readPcm(20e-3 * 44100);
|
|
|
|
|
PcmPointer += 20e-3 * 44100;
|
|
|
|
|
|
|
|
|
|
// In case of Scottie, skip first sync pulse
|
|
|
|
|
if (VISmap[VIS] == S1 || VISmap[VIS] == S2 || VISmap[VIS] == SDX) {
|
|
|
|
|
readPcm(9e-3 * 44100);
|
|
|
|
|
PcmPointer += 9e-3 * 44100;
|
|
|
|
|
}
|
2011-07-07 14:09:57 +00:00
|
|
|
|
|
2011-07-29 20:09:42 +00:00
|
|
|
|
if (VISmap[VIS] != UNKNOWN) return VISmap[VIS];
|
|
|
|
|
else printf(" No VIS found\n");
|
2011-08-11 11:48:35 +00:00
|
|
|
|
return 0;
|
2011-07-07 14:09:57 +00:00
|
|
|
|
}
|
2011-08-02 15:50:37 +00:00
|
|
|
|
|
|
|
|
|
|