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>
|
2011-07-19 19:29:14 +00:00
|
|
|
#include <string.h>
|
2011-07-07 14:09:57 +00:00
|
|
|
#include <fftw3.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
|
|
|
#include "common.h"
|
|
|
|
|
2011-08-12 20:06:23 +00:00
|
|
|
/* Find the slant angle of the sync singnal and adjust sample rate to cancel it out
|
2011-07-07 14:09:57 +00:00
|
|
|
* Length: number of PCM samples to process
|
|
|
|
* Mode: one of M1, M2, S1, S2, R72, R36 ...
|
|
|
|
* Rate: approximate sampling rate used
|
|
|
|
* Skip: pointer to variable where the skip amount will be returned
|
|
|
|
* returns adjusted sample rate
|
2011-08-20 05:51:27 +00:00
|
|
|
*
|
2011-07-07 14:09:57 +00:00
|
|
|
*/
|
2011-08-18 00:04:28 +00:00
|
|
|
double FindSync (guchar Mode, double Rate, int *Skip) {
|
2011-07-07 14:09:57 +00:00
|
|
|
|
2011-08-13 12:26:49 +00:00
|
|
|
int LineWidth = ModeSpec[Mode].LineLen / ModeSpec[Mode].SyncLen * 4;
|
|
|
|
int x,y,xmid,x0;
|
2012-04-06 12:38:41 +00:00
|
|
|
int q, d, qMost, dMost, s;
|
|
|
|
gushort xAcc[700] = {0}, xmax;
|
2011-08-13 12:26:49 +00:00
|
|
|
gushort lines[600][(MAXSLANT-MINSLANT)*2];
|
|
|
|
gushort cy, cx, Retries = 0;
|
2011-08-20 11:49:29 +00:00
|
|
|
bool SyncImg[700][630] = {{false}};
|
2011-08-18 00:04:28 +00:00
|
|
|
double t=0, slantAngle;
|
2011-07-07 14:09:57 +00:00
|
|
|
|
2011-08-20 05:51:27 +00:00
|
|
|
|
2011-07-07 14:09:57 +00:00
|
|
|
// Repeat until slant < 0.5° or until we give up
|
2011-08-20 11:49:29 +00:00
|
|
|
while (true) {
|
2011-08-18 00:04:28 +00:00
|
|
|
|
2011-08-17 16:33:04 +00:00
|
|
|
// Draw the 2D sync signal at current rate
|
2011-07-07 14:09:57 +00:00
|
|
|
|
2011-08-17 16:33:04 +00:00
|
|
|
for (y=0; y<ModeSpec[Mode].ImgHeight; y++) {
|
|
|
|
for (x=0; x<LineWidth; x++) {
|
2011-08-20 05:51:27 +00:00
|
|
|
t = (y + 1.0*x/LineWidth) * ModeSpec[Mode].LineLen;
|
|
|
|
// Center sync pulse horizontally
|
|
|
|
if (y>0 || x>=LineWidth/2) SyncImg[x][y] = HasSync[ (int)( (t-ModeSpec[Mode].LineLen/2) / 1.5e-3 * Rate/44100) ];
|
2011-07-07 14:09:57 +00:00
|
|
|
}
|
|
|
|
}
|
2011-07-25 22:41:04 +00:00
|
|
|
|
2011-07-07 14:09:57 +00:00
|
|
|
/** Linear Hough transform **/
|
|
|
|
|
|
|
|
dMost = qMost = 0;
|
2011-08-13 12:26:49 +00:00
|
|
|
memset(lines, 0, sizeof(lines[0][0]) * (MAXSLANT-MINSLANT)*2 * 600);
|
2011-07-07 14:09:57 +00:00
|
|
|
|
2011-07-17 23:12:42 +00:00
|
|
|
// Find white pixels
|
2011-08-17 16:33:04 +00:00
|
|
|
for (cy = 0; cy < ModeSpec[Mode].ImgHeight; cy++) {
|
2011-07-25 22:41:04 +00:00
|
|
|
for (cx = 0; cx < LineWidth; cx++) {
|
2011-07-17 23:12:42 +00:00
|
|
|
if (SyncImg[cx][cy]) {
|
2011-07-07 14:09:57 +00:00
|
|
|
|
|
|
|
// Slant angles to consider
|
2011-07-17 23:12:42 +00:00
|
|
|
for (q = MINSLANT*2; q < MAXSLANT*2; q ++) {
|
|
|
|
|
|
|
|
// Line accumulator
|
2011-07-25 22:41:04 +00:00
|
|
|
d = LineWidth + round( -cx * sin(deg2rad(q/2.0)) + cy * cos(deg2rad(q/2.0)) );
|
2011-07-28 12:45:37 +00:00
|
|
|
if (d > 0 && d < LineWidth) {
|
2011-07-17 23:12:42 +00:00
|
|
|
lines[d][q-MINSLANT*2] ++;
|
|
|
|
if (lines[d][q-MINSLANT*2] > lines[dMost][qMost-MINSLANT*2]) {
|
|
|
|
dMost = d;
|
|
|
|
qMost = q;
|
|
|
|
}
|
2011-07-07 14:09:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( qMost == 0) {
|
|
|
|
printf(" no sync signal; giving up\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
slantAngle = qMost / 2.0;
|
|
|
|
|
2011-08-18 00:04:28 +00:00
|
|
|
printf(" %.1f° (d=%d) @ %.1f Hz", slantAngle, dMost, Rate);
|
2011-07-07 14:09:57 +00:00
|
|
|
|
2011-08-18 00:04:28 +00:00
|
|
|
// Adjust sample rate
|
|
|
|
Rate = Rate + tan(deg2rad(90 - slantAngle)) / LineWidth * Rate;
|
2011-07-07 14:09:57 +00:00
|
|
|
|
|
|
|
if (slantAngle > 89 && slantAngle < 91) {
|
2011-07-25 22:41:04 +00:00
|
|
|
printf(" slant OK :)\n");
|
2011-07-07 14:09:57 +00:00
|
|
|
break;
|
|
|
|
} else if (Retries == 3) {
|
|
|
|
printf(" still slanted; giving up\n");
|
2011-08-16 21:29:38 +00:00
|
|
|
Rate = 44100;
|
|
|
|
printf(" -> 44100\n");
|
2011-07-07 14:09:57 +00:00
|
|
|
break;
|
|
|
|
}
|
2011-08-18 00:04:28 +00:00
|
|
|
printf(" -> %.1f recalculating\n", Rate);
|
|
|
|
Retries ++;
|
2011-07-07 14:09:57 +00:00
|
|
|
}
|
2011-08-20 05:51:27 +00:00
|
|
|
|
|
|
|
// find abscissa at higher resolution
|
2011-07-28 12:45:37 +00:00
|
|
|
memset(xAcc, 0, sizeof(xAcc[0]) * 700);
|
2011-08-18 00:04:28 +00:00
|
|
|
xmax = 0;
|
|
|
|
|
|
|
|
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 / 1.5e-3 * Rate/44100) ];
|
2011-07-28 12:45:37 +00:00
|
|
|
if (xAcc[x] > xAcc[xmax]) xmax = x;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// find center of sync pulse
|
|
|
|
x0 = -1;
|
|
|
|
xmid=-1;
|
|
|
|
for (x=0;x<700;x++) {
|
|
|
|
if (xAcc[x] >= xAcc[xmax]*0.5 && x0==-1) x0 = x;
|
|
|
|
if (x0 != -1 && xAcc[x] < xAcc[xmax]*0.5) {
|
|
|
|
xmid = (x + x0) / 2;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// skip until the start of the sync pulse
|
|
|
|
s = (xmid / 700.0 * ModeSpec[Mode].LineLen - ModeSpec[Mode].SyncLen/2) * Rate;
|
|
|
|
|
|
|
|
// Scottie modes don't start lines with the sync pulse
|
|
|
|
if (Mode == S1 || Mode == S2 || Mode == SDX)
|
|
|
|
s -= 2 * (ModeSpec[Mode].SeparatorLen + ModeSpec[Mode].PixelLen*ModeSpec[Mode].ImgWidth) * Rate;
|
|
|
|
|
|
|
|
*Skip = s;
|
|
|
|
|
2011-07-07 14:09:57 +00:00
|
|
|
return (Rate);
|
|
|
|
|
|
|
|
}
|