mad rewrite of video timing logic

pull/5/head
Oona 2013-01-20 19:59:43 +02:00
rodzic 06748fe211
commit 5372b4b97a
8 zmienionych plików z 156 dodań i 138 usunięć

Wyświetl plik

@ -11,7 +11,7 @@ OBJECTS = common.o modespec.o gui.o video.o vis.o sync.o pcm.o fsk.o slowrx.o
all: slowrx
slowrx: $(OBJECTS)
$(CC) $(CFLAGS) -o $@ $(OBJECTS) $(GTKLIBS) -lfftw3 -lgthread-2.0 -lpnglite -lasound -lm
$(CC) $(CFLAGS) -o $@ $(OBJECTS) $(GTKLIBS) -lfftw3 -lgthread-2.0 -lasound -lm
%.o: %.c common.h
$(CC) $(CFLAGS) $(GTKCFLAGS) $(OFLAGS) -c -o $@ $<

Wyświetl plik

@ -132,7 +132,7 @@ void *Listen ();
void populateDeviceList ();
void readPcm (gint numsamples);
void saveCurrentPic();
void setVU (short int PcmValue, double SNRdB);
void setVU (short int PcmValue, int WinIdx);
void evt_AbortRx ();
void evt_changeDevices ();

13
gui.c
Wyświetl plik

@ -77,16 +77,16 @@ void createGUI() {
gtk_entry_set_text(GTK_ENTRY(gui.entry_picdir),g_key_file_get_string(config,"slowrx","rxdir",NULL));
}
setVU(0, -100);
setVU(0, 6);
gtk_widget_show_all (gui.window_main);
}
// Draw signal level meters according to given values
void setVU (short int PcmValue, double SNRdB) {
void setVU (short int PcmValue, int WinIdx) {
int x,y;
int PWRdB = (int)round(10 * log10(pow(PcmValue/32767.0,2)));
int PWRdB;
guchar *pixelsPWR, *pixelsSNR, *pPWR, *pSNR;
unsigned int rowstridePWR,rowstrideSNR;
@ -96,6 +96,11 @@ void setVU (short int PcmValue, double SNRdB) {
rowstrideSNR = gdk_pixbuf_get_rowstride (pixbuf_SNR);
pixelsSNR = gdk_pixbuf_get_pixels (pixbuf_SNR);
if (PcmValue == 0)
PWRdB = 0;
else
PWRdB = (int)round(10 * log10(pow(PcmValue/32767.0,2)));
for (y=0; y<20; y++) {
for (x=0; x<100; x++) {
@ -112,7 +117,7 @@ void setVU (short int PcmValue, double SNRdB) {
pPWR[0] = pPWR[1] = pPWR[2] = 0x80;
}
if (SNRdB >= -0.6*x+40) {
if ((6-WinIdx)/0.06 > 100-x) {
pSNR[0] = 0xef;
pSNR[1] = 0xe4;
pSNR[2] = 0x34;

1
pcm.c
Wyświetl plik

@ -183,6 +183,7 @@ int initPcmDevice(char *wanteddevname) {
}
pcm.Buffer = calloc( BUFLEN, sizeof(gint16));
memset(pcm.Buffer, 0, BUFLEN);
if (exact_rate != 44100) {
fprintf(stderr, "ALSA: Got %d Hz instead of 44100. Expect artifacts.\n", exact_rate);

Wyświetl plik

@ -83,14 +83,14 @@ void *Listen() {
// Allocate space for cached Lum
free(StoredLum);
StoredLum = calloc( (int)(ModeSpec[CurrentPic.Mode].LineLen * ModeSpec[CurrentPic.Mode].ImgHeight + 1) * 44100, sizeof(guchar));
StoredLum = calloc( (int)((ModeSpec[CurrentPic.Mode].LineLen * ModeSpec[CurrentPic.Mode].ImgHeight + 1) * 44100), sizeof(guchar));
if (StoredLum == NULL) {
perror("Listen: Unable to allocate memory for Lum");
exit(EXIT_FAILURE);
}
// Allocate space for sync signal
HasSync = calloc((int)(ModeSpec[CurrentPic.Mode].LineLen * ModeSpec[CurrentPic.Mode].ImgHeight / SYNCPIXLEN +1), sizeof(gboolean));
HasSync = calloc((int)(ModeSpec[CurrentPic.Mode].LineLen * ModeSpec[CurrentPic.Mode].ImgHeight / (13.0/44100) +1), sizeof(gboolean));
if (HasSync == NULL) {
perror("Listen: Unable to allocate memory for sync signal");
exit(EXIT_FAILURE);
@ -136,7 +136,7 @@ void *Listen() {
if (Finished && gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gui.tog_slant))) {
// Fix slant
setVU(0,-100);
setVU(0,6);
gdk_threads_enter ();
gtk_statusbar_push (GTK_STATUSBAR(gui.statusbar), 0, "Calculating slant..." );
gtk_widget_set_sensitive (gui.grid_vu, FALSE);
@ -163,7 +163,7 @@ void *Listen() {
// Save PNG
if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(gui.tog_save))) {
setVU(0,-100);
setVU(0,6);
/*ensure_dir_exists("rx-lum");
LumFile = fopen(lumfilename,"w");

8
sync.c
Wyświetl plik

@ -23,7 +23,7 @@ double FindSync (guchar Mode, double Rate, int *Skip) {
gushort xAcc[700] = {0};
gushort lines[600][(MAXSLANT-MINSLANT)*2];
gushort cy, cx, Retries = 0;
gboolean SyncImg[700][630] = {{FALSE}};
gboolean SyncImg[700][630] = {{FALSE}};
double t=0, slantAngle, s;
double ConvoFilter[8] = { 1,1,1,1,-1,-1,-1,-1 };
double convd, maxconvd=0;
@ -37,7 +37,7 @@ double FindSync (guchar Mode, double Rate, int *Skip) {
for (y=0; y<ModeSpec[Mode].ImgHeight; y++) {
for (x=0; x<LineWidth; x++) {
t = (y + 1.0*x/LineWidth) * ModeSpec[Mode].LineLen;
SyncImg[x][y] = HasSync[ (int)( t / SYNCPIXLEN * Rate/44100) ];
SyncImg[x][y] = HasSync[ (int)( t * Rate / 13.0) ];
}
}
@ -98,7 +98,7 @@ double FindSync (guchar Mode, double Rate, int *Skip) {
for (y=0; y<ModeSpec[Mode].ImgHeight; y++) {
for (x=0; x<700; x++) {
t = y * ModeSpec[Mode].LineLen + x/700.0 * ModeSpec[Mode].LineLen;
xAcc[x] += HasSync[ (int)(t / SYNCPIXLEN * Rate/44100) ];
xAcc[x] += HasSync[ (int)(t / (13.0/44100) * Rate/44100) ];
}
}
@ -125,6 +125,8 @@ double FindSync (guchar Mode, double Rate, int *Skip) {
+ ModeSpec[Mode].PorchLen * 2;
*Skip = s * Rate;
printf("will return %.2f\n",Rate);
return (Rate);

249
video.c
Wyświetl plik

@ -25,20 +25,31 @@ gboolean GetVideo(guchar Mode, double Rate, int Skip, gboolean Redraw) {
guint i=0, j=0;
guint FFTLen=1024, WinLength=0;
guint LopassBin,SyncTargetBin;
int LineNum = 0, SampleNum, Length;
int x = 0, y = 0, prevline=0, tx=0;
int SampleNum, Length, NumChans;
int x = 0, y = 0, tx=0, k=0;
double Hann[7][1024] = {{0}};
double t=0, Freq = 0, PrevFreq = 0, InterpFreq = 0, NextPixelTime = 0, NextSNRtime = 0;
double NextSyncTime = 0;
double Freq = 0, PrevFreq = 0, InterpFreq = 0;
int NextSNRtime = 0, NextSyncTime = 0;
double Praw, Psync;
double Power[1024] = {0};
double Pvideo_plus_noise=0, Pnoise_only=0, Pnoise=0, Psignal=0;
double SNR = 0;
double CurLineTime = 0;
double ChanStart[4] = {0}, ChanLen[4] = {0};
guchar Image[800][616][3] = {{{0}}};
guchar Channel = 0, WinIdx = 0;
typedef struct {
int X;
int Y;
int Time;
guchar Channel;
gboolean Last;
} _PixelGrid;
_PixelGrid *PixelGrid;
PixelGrid = calloc( ModeSpec[Mode].ImgWidth * ModeSpec[Mode].ImgHeight * 3, sizeof(_PixelGrid) );
// Initialize Hann windows of different lengths
gushort HannLens[7] = { 48, 64, 96, 128, 256, 512, 1024 };
for (j = 0; j < 7; j++)
@ -46,7 +57,6 @@ gboolean GetVideo(guchar Mode, double Rate, int Skip, gboolean Redraw) {
Hann[j][i] = 0.5 * (1 - cos( (2 * M_PI * i) / (HannLens[j] - 1)) );
// Starting times of video channels on every line, counted from beginning of line
switch (Mode) {
case R36:
@ -76,6 +86,73 @@ gboolean GetVideo(guchar Mode, double Rate, int Skip, gboolean Redraw) {
}
// Number of channels per line
switch(Mode) {
case R24BW:
case R12BW:
case R8BW:
NumChans = 1;
break;
case R24:
case R36:
NumChans = 2;
break;
default:
NumChans = 3;
break;
}
// Plan ahead the time instants (in samples) at which to take pixels out
int PixelIdx = 0;
for (y=0; y<ModeSpec[Mode].ImgHeight; y++) {
for (Channel=0; Channel<NumChans; Channel++) {
for (x=0; x<ModeSpec[Mode].ImgWidth; x++) {
if (Mode == R36 || Mode == R24) {
if (Channel == 1) {
if (y % 2 == 0) PixelGrid[PixelIdx].Channel = 1;
else PixelGrid[PixelIdx].Channel = 2;
} else PixelGrid[PixelIdx].Channel = 0;
} else {
PixelGrid[PixelIdx].Channel = Channel;
}
PixelGrid[PixelIdx].Time = (int)round(Rate * (y * ModeSpec[Mode].LineLen + ChanStart[Channel] +
(1.0*(x-.5)/ModeSpec[Mode].ImgWidth*ChanLen[PixelGrid[PixelIdx].Channel]))) + Skip;
PixelGrid[PixelIdx].X = x;
PixelGrid[PixelIdx].Y = y;
PixelGrid[PixelIdx].Last = FALSE;
PixelIdx ++;
}
}
}
PixelGrid[PixelIdx-1].Last = TRUE;
for (k=0; k<PixelIdx; k++) {
if (PixelGrid[k].Time >= 0) {
PixelIdx = k;
break;
}
}
/*case PD50:
case PD90:
case PD120:
case PD160:
case PD180:
case PD240:
case PD290:
if (CurLineTime >= ChanStart[2] + ChanLen[2]) Channel = 3; // ch 0 of even line
else if (CurLineTime >= ChanStart[2]) Channel = 2;
else if (CurLineTime >= ChanStart[1]) Channel = 1;
else Channel = 0;
break;*/
// Initialize pixbuffer
if (!Redraw) {
g_object_unref(pixbuf_rx);
@ -86,7 +163,7 @@ gboolean GetVideo(guchar Mode, double Rate, int Skip, gboolean Redraw) {
int rowstride = gdk_pixbuf_get_rowstride (pixbuf_rx);
guchar *pixels, *p;
pixels = gdk_pixbuf_get_pixels(pixbuf_rx);
g_object_unref(pixbuf_disp);
pixbuf_disp = gdk_pixbuf_scale_simple(pixbuf_rx, 500,
500.0/ModeSpec[Mode].ImgWidth * ModeSpec[Mode].ImgHeight * ModeSpec[Mode].YScale, GDK_INTERP_BILINEAR);
@ -104,10 +181,6 @@ gboolean GetVideo(guchar Mode, double Rate, int Skip, gboolean Redraw) {
// Loop through signal
for (SampleNum = 0; SampleNum < Length; SampleNum++) {
t = (SampleNum - Skip) / Rate;
CurLineTime = fmod(t, ModeSpec[Mode].LineLen);
if (!Redraw) {
/*** Read ahead from sound card ***/
@ -117,7 +190,7 @@ gboolean GetVideo(guchar Mode, double Rate, int Skip, gboolean Redraw) {
/*** Store the sync band for later adjustments ***/
if (t >= NextSyncTime) {
if (SampleNum == NextSyncTime) {
Praw = Psync = 0;
@ -145,15 +218,19 @@ gboolean GetVideo(guchar Mode, double Rate, int Skip, gboolean Redraw) {
if (Psync > 2*Praw) HasSync[SyncSampleNum] = TRUE;
else HasSync[SyncSampleNum] = FALSE;
NextSyncTime += SYNCPIXLEN;
NextSyncTime += 13;
SyncSampleNum ++;
}
/*** Estimate SNR ***/
if (t >= NextSNRtime) {
if (SampleNum == NextSNRtime) {
memset(in, 0, sizeof(double)*FFTLen);
memset(out, 0, sizeof(double)*FFTLen);
// Apply Hann window
for (i = 0; i < FFTLen; i++) in[i] = pcm.Buffer[pcm.WindowPtr + i - FFTLen/2] / 32768.0 * Hann[6][i];
@ -191,13 +268,14 @@ gboolean GetVideo(guchar Mode, double Rate, int Skip, gboolean Redraw) {
// Lower bound to -20 dB
SNR = ((Psignal / Pnoise < .01) ? -20 : 10 * log10(Psignal / Pnoise));
NextSNRtime += 8e-3;
NextSNRtime += 256;
}
/*** FM demodulation ***/
if (t >= NextPixelTime) {
if (SampleNum % 6 == 0) { // Take FFT every 6 samples
PrevFreq = Freq;
@ -219,6 +297,7 @@ gboolean GetVideo(guchar Mode, double Rate, int Skip, gboolean Redraw) {
WinLength = HannLens[WinIdx];
memset(in, 0, sizeof(double)*FFTLen);
memset(out, 0, sizeof(double)*FFTLen);
// Apply window function
@ -242,135 +321,71 @@ gboolean GetVideo(guchar Mode, double Rate, int Skip, gboolean Redraw) {
(2 * log( pow(Power[MaxBin], 2) / (Power[MaxBin + 1] * Power[MaxBin - 1])));
// In Hertz
Freq = Freq / FFTLen * 44100;
InterpFreq = Freq;
} else {
// Clip if out of bounds
Freq = ( (MaxBin > GetBin(1900 + CurrentPic.HedrShift, FFTLen)) ? 2300 : 1500 ) + CurrentPic.HedrShift;
}
}
// Linear interpolation of (chronologically) intermediate frequencies
InterpFreq = PrevFreq + (Freq-PrevFreq) * ((t-(NextPixelTime-ModeSpec[Mode].PixelLen))/ModeSpec[Mode].PixelLen);
} /* endif (SampleNum == PixelGrid[PixelIdx].Time) */
// Linear interpolation of (chronologically) intermediate frequencies, for redrawing
//InterpFreq = PrevFreq + (Freq-PrevFreq) * ... // TODO!
// Calculate luminency & store for later use
StoredLum[SampleNum] = clip((InterpFreq - (1500 + CurrentPic.HedrShift)) / 3.1372549);
StoredLum[SampleNum] = clip((Freq - (1500 + CurrentPic.HedrShift)) / 3.1372549);
}
} /* endif (!Redraw) */
/*** Are we on a video line, and should we sample a pixel? ***/
if ( ((CurLineTime >= ChanStart[0] && CurLineTime < ChanStart[0] + ChanLen[0])
|| (CurLineTime >= ChanStart[1] && CurLineTime < ChanStart[1] + ChanLen[1])
|| (CurLineTime >= ChanStart[2] && CurLineTime < ChanStart[2] + ChanLen[2]) )
&& t >= NextPixelTime
) {
if (SampleNum == PixelGrid[PixelIdx].Time) {
LineNum = t / ModeSpec[Mode].LineLen;
x = PixelGrid[PixelIdx].X;
y = PixelGrid[PixelIdx].Y;
Channel = PixelGrid[PixelIdx].Channel;
// Store pixel
Image[x][y][Channel] = StoredLum[SampleNum];
// Which channel is this?
switch(Mode) {
case R24BW:
case R12BW:
case R8BW:
Channel = 0;
break;
case R36:
case R24:
if (CurLineTime >= ChanStart[1]) {
if (LineNum % 2 == 0) Channel = 1;
else Channel = 2;
} else Channel = 0;
break;
case PD50:
case PD90:
case PD120:
case PD160:
case PD180:
case PD240:
case PD290:
if (CurLineTime >= ChanStart[2] + ChanLen[2]) Channel = 3; // ch 0 of even line
else if (CurLineTime >= ChanStart[2]) Channel = 2;
else if (CurLineTime >= ChanStart[1]) Channel = 1;
else Channel = 0;
break;
default:
if (CurLineTime >= ChanStart[2]) Channel = 2;
else if (CurLineTime >= ChanStart[1]) Channel = 1;
else Channel = 0;
break;
}
// X coordinate of this pixel
x = (CurLineTime - ChanStart[Channel]) / ChanLen[Channel] * ModeSpec[Mode].ImgWidth;
// Y coordinate of this pixel
switch(Channel) {
case 3:
y = LineNum + 1;
Channel = 0;
break;
default:
y = LineNum;
break;
}
// Store pixel
if (x >= 0 && y >= 0 && x < ModeSpec[Mode].ImgWidth) {
Image[x][y][Channel] = StoredLum[SampleNum];
// Some modes have R-Y & B-Y channels that are twice the height of the Y channel
if (Channel > 0)
switch(Mode) {
case R36:
case R24:
if (y < ModeSpec[Mode].ImgHeight-1) Image[x][y+1][Channel] = StoredLum[SampleNum];
break;
}
}
if (y > ModeSpec[Mode].ImgHeight-1) break;
// Some modes have R-Y & B-Y channels that are twice the height of the Y channel
if (Channel > 0 && (Mode == R36 || Mode == R24))
Image[x][y+1][Channel] = StoredLum[SampleNum];
// Calculate and draw pixels to pixbuf on line change
if (LineNum != prevline || (LineNum == ModeSpec[Mode].ImgHeight-1 && x == ModeSpec[Mode].ImgWidth-1)) {
if (x == ModeSpec[Mode].ImgWidth-1 || PixelGrid[PixelIdx].Last) {
for (tx = 0; tx < ModeSpec[Mode].ImgWidth; tx++) {
p = pixels + prevline * rowstride + tx * 3;
p = pixels + y * rowstride + tx * 3;
switch(ModeSpec[Mode].ColorEnc) {
case RGB:
p[0] = Image[tx][prevline][0];
p[1] = Image[tx][prevline][1];
p[2] = Image[tx][prevline][2];
p[0] = Image[tx][y][0];
p[1] = Image[tx][y][1];
p[2] = Image[tx][y][2];
break;
case GBR:
p[0] = Image[tx][prevline][2];
p[1] = Image[tx][prevline][0];
p[2] = Image[tx][prevline][1];
p[0] = Image[tx][y][2];
p[1] = Image[tx][y][0];
p[2] = Image[tx][y][1];
break;
case YUV:
p[0] = clip((100 * Image[tx][prevline][0] + 140 * Image[tx][prevline][1] - 17850) / 100.0);
p[1] = clip((100 * Image[tx][prevline][0] - 71 * Image[tx][prevline][1] - 33 *
Image[tx][prevline][2] + 13260) / 100.0);
p[2] = clip((100 * Image[tx][prevline][0] + 178 * Image[tx][prevline][2] - 22695) / 100.0);
p[0] = clip((100 * Image[tx][y][0] + 140 * Image[tx][y][1] - 17850) / 100.0);
p[1] = clip((100 * Image[tx][y][0] - 71 * Image[tx][y][1] - 33 *
Image[tx][y][2] + 13260) / 100.0);
p[2] = clip((100 * Image[tx][y][0] + 178 * Image[tx][y][2] - 22695) / 100.0);
break;
case BW:
p[0] = p[1] = p[2] = Image[tx][prevline][0];
p[0] = p[1] = p[2] = Image[tx][y][0];
break;
}
}
if (!Redraw || LineNum % 5 == 0 || LineNum == ModeSpec[Mode].ImgHeight-1) {
if (!Redraw || y % 5 == 0 || PixelGrid[PixelIdx].Last) {
// Scale and update image
g_object_unref(pixbuf_disp);
pixbuf_disp = gdk_pixbuf_scale_simple(pixbuf_rx, 500,
@ -382,17 +397,13 @@ gboolean GetVideo(guchar Mode, double Rate, int Skip, gboolean Redraw) {
}
}
if (LineNum > prevline)
NextPixelTime = t + ModeSpec[Mode].PixelLen/3; // take 3 samples per pixel; to better allow for redraw
else
NextPixelTime += ModeSpec[Mode].PixelLen/3;
PixelIdx ++;
prevline = LineNum;
}
} /* endif (SampleNum == PixelGrid[PixelIdx].Time) */
if (!Redraw && SampleNum % 8820 == 0) {
setVU(pcm.PeakVal, SNR);
setVU(pcm.PeakVal, WinIdx);
pcm.PeakVal = 0;
}

11
vis.c
Wyświetl plik

@ -18,12 +18,10 @@
guchar GetVIS () {
int selmode, ptr=0;
//int Pointer = 0;
int VIS = 0, Parity = 0, HedrPtr = 0;
//gushort pcm.PeakVal = 0;
guint FFTLen = 2048, i=0, j=0, k=0, MaxBin = 0;
double Power[2048] = {0}, HedrBuf[100] = {0}, tone[100] = {0}, Hann[882] = {0};
gboolean gotvis = FALSE;
gboolean gotvis = FALSE;
guchar Bit[8] = {0}, ParityBit = 0;
for (i = 0; i < FFTLen; i++) in[i] = 0;
@ -56,11 +54,12 @@ guchar GetVIS () {
MaxBin = 0;
for (i = GetBin(500, FFTLen); i <= GetBin(3300, FFTLen); i++) {
Power[i] = pow(out[i], 2) + pow(out[FFTLen - i], 2);
if (Power[i] > Power[MaxBin] || MaxBin == 0) MaxBin = i;
if (MaxBin == 0 || Power[i] > Power[MaxBin]) MaxBin = i;
}
// Find the peak frequency by Gaussian interpolation
if (MaxBin > GetBin(500, FFTLen) && MaxBin < GetBin(3300, FFTLen))
if (MaxBin > GetBin(500, FFTLen) && MaxBin < GetBin(3300, FFTLen) &&
Power[MaxBin] > 0 && Power[MaxBin+1] > 0 && Power[MaxBin-1] > 0)
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];
@ -157,7 +156,7 @@ guchar GetVIS () {
}
if (++ptr == 25) {
setVU(pcm.PeakVal, -20);
setVU(pcm.PeakVal, 6);
pcm.PeakVal = 0;
ptr = 0;
}