working sync align, chroma filtering, etc.

dev
Oona Räisänen 2015-08-30 16:56:20 +03:00
rodzic 122a0add97
commit bc17b64b6d
9 zmienionych plików z 558 dodań i 455 usunięć

Wyświetl plik

@ -1,4 +1,4 @@
bin_PROGRAMS = slowrx
slowrx_CPPFLAGS = -g $(GTKMM_CFLAGS) @SNDFILE_CFLAGS@ $(PORTAUDIO_CFLAGS)
slowrx_CPPFLAGS = -Wall -Wextra -pedantic -g $(GTKMM_CFLAGS) @SNDFILE_CFLAGS@ $(PORTAUDIO_CFLAGS)
slowrx_LDADD = $(GTKMM_LIBS) @SNDFILE_LIBS@ -lfftw3 $(PORTAUDIO_LIBS)
slowrx_SOURCES = slowrx.cc common.cc modespec.cc gui.cc dsp.cc header.cc video.cc sync.cc tests.cc

Wyświetl plik

@ -3,11 +3,8 @@
bool Abort = false;
bool Adaptive = true;
bool *HasSync = NULL;
uint16_t HedrShift = 0;
bool ManualActivated = false;
bool ManualResync = false;
int *StoredLum = NULL;
Glib::RefPtr<Gdk::Pixbuf> pixbuf_PWR;
Glib::RefPtr<Gdk::Pixbuf> pixbuf_SNR;
@ -18,16 +15,19 @@ Glib::KeyFile config;
std::vector<std::thread> threads(2);
PicMeta CurrentPic;
std::vector<std::vector<double> > DSPworker::window_ (16);
// Clip to [0..255]
int clip (double a) {
guint8 clip (double a) {
if (a < 0) return 0;
else if (a > 255) return 255;
return round(a);
}
double fclip (double a) {
if (a < 0.0) return 0.0;
else if (a > 1.0) return 1.0;
return a;
}
// Convert degrees -> radians
double deg2rad (double Deg) {
@ -97,15 +97,15 @@ void evt_AbortRx() {
}
// Another device selected from list
void evt_changeDevices() {
void evt_changeDevices() {/*
int status;
Abort = true;
static int init;
/*if (init)
threads[0].join;*/
if (init)
threads[0].join;
init = 1;
// if (pcm.handle != NULL) snd_pcm_close(pcm.handle);
@ -130,7 +130,7 @@ void evt_changeDevices() {
//config->set_string("slowrx","device",gui.combo_card->get_active_text());
//threads[0] = thread(Listen);
//threads[0] = thread(Listen);*/
}
@ -145,13 +145,13 @@ void evt_clearPix() {
// Manual slant adjust
void evt_clickimg(Gtk::Widget *widget, GdkEventButton* event, Gdk::WindowEdge edge) {
static double prevx=0,prevy=0,newrate;
/*static double prevx=0,prevy=0,newrate;
static bool secondpress=false;
double x,y,dx,dy,xic;
(void)widget;
(void)edge;
/*
if (event->type == Gdk::BUTTON_PRESS && event->button == 1 && gui.tog_setedge->get_active()) {
x = event->x * (ModeSpec[CurrentPic.Mode].ImgWidth / 500.0);

Wyświetl plik

@ -19,13 +19,13 @@
struct Point {
int x;
int y;
explicit Point(int x = 0.0, int y=0.0) : x(x), y(y) {}
explicit Point(int _x = 0, int _y=0) : x(_x), y(_y) {}
};
struct Tone {
double dur;
double freq;
explicit Tone(double dur = 0.0, double freq=0.0) : dur(dur), freq(freq) {}
explicit Tone(double _dur = 0.0, double _freq=0.0) : dur(_dur), freq(_freq) {}
};
using Wave = std::vector<double>;
@ -75,6 +75,39 @@ enum eVISParity {
extern std::map<int, SSTVMode> vis2mode;
typedef struct ModeSpec {
std::string Name;
std::string ShortName;
double tSync;
double tPorch;
double tSep;
double tScan;
double tLine;
size_t ScanPixels;
size_t NumLines;
size_t HeaderLines;
eColorEnc ColorEnc;
eSyncOrder SyncOrder;
eSubSamp SubSampling;
eVISParity VISParity;
} _ModeSpec;
extern _ModeSpec ModeSpec[];
struct Picture {
SSTVMode mode;
Wave video_signal;
double video_dt;
std::vector<bool> sync_signal;
double sync_dt;
double speed;
double starts_at;
explicit Picture(SSTVMode _mode)
: mode(_mode), video_dt(ModeSpec[_mode].tScan/ModeSpec[_mode].ScanPixels/2),
sync_dt(ModeSpec[_mode].tLine / ModeSpec[_mode].ScanPixels/3), speed(1) {}
};
class DSPworker {
public:
@ -88,15 +121,20 @@ class DSPworker {
double forward ();
double forward_time (double);
void forward_to_time (double);
void set_fshift (double);
void windowedMoment (WindowType, fftw_complex *);
double peakFreq (double, double, WindowType);
int freq2bin (double, int);
std::vector<double> bandPowerPerHz (std::vector<std::vector<double> >);
std::vector<double> bandPowerPerHz (std::vector<std::vector<double> >, WindowType wintype=WINDOW_HANN2047);
WindowType bestWindowFor (SSTVMode, double SNR=99);
double videoSNR();
double lum(SSTVMode, bool is_adaptive=false);
bool is_open ();
bool is_open ();
double get_t ();
bool isLive();
bool hasSync();
private:
@ -106,16 +144,22 @@ class DSPworker {
int cirbuf_tail_;
int cirbuf_fill_count_;
bool please_stop_;
short *read_buffer_;
SndfileHandle file_;
fftw_complex *fft_inbuf_;
fftw_complex *fft_outbuf_;
fftw_plan fft_plan_small_;
fftw_plan fft_plan_big_;
double samplerate_;
size_t num_chans_;
PaStream *pa_stream_;
eStreamType stream_type_;
bool is_open_;
double t_;
double fshift_;
double next_snr_time_;
double SNR_;
WindowType sync_window_;
static std::vector<std::vector<double> > window_;
};
@ -173,44 +217,22 @@ extern Gtk::ListStore *savedstore;
extern Glib::KeyFile config;
typedef struct _PicMeta PicMeta;
struct _PicMeta {
int HedrShift;
int Mode;
double Rate;
int Skip;
Gdk::Pixbuf *thumbbuf;
char timestr[40];
};
extern PicMeta CurrentPic;
typedef struct {
Point pt;
int Channel;
double Time;
} PixelSample;
typedef struct ModeSpec {
std::string Name;
std::string ShortName;
double tSync;
double tPorch;
double tSep;
double tScan;
double tLine;
int ScanPixels;
int NumLines;
int HeaderLines;
eColorEnc ColorEnc;
eSyncOrder SyncOrder;
eSubSamp SubSampling;
eVISParity VISParity;
} _ModeSpec;
extern _ModeSpec ModeSpec[];
std::vector<PixelSample> getPixelSamplingPoints(SSTVMode mode);
double power (fftw_complex coeff);
int clip (double a);
guint8 clip (double a);
double fclip (double a);
void createGUI ();
double deg2rad (double Deg);
std::string GetFSK ();
bool GetVideo (SSTVMode Mode, DSPworker *dsp);
SSTVMode modeFromNextHeader (DSPworker*);
bool rxVideo (SSTVMode Mode, DSPworker *dsp);
SSTVMode nextHeader (DSPworker*);
int initPcmDevice (std::string);
void *Listen ();
void populateDeviceList ();
@ -218,25 +240,28 @@ void readPcm (int numsamples);
void saveCurrentPic();
void setVU (double *Power, int FFTLen, int WinIdx, bool ShowWin);
int startGui (int, char**);
void findSyncRansac (SSTVMode, std::vector<bool>);
void findSyncHough (SSTVMode, std::vector<bool>);
double findSyncAutocorr (SSTVMode, std::vector<bool>);
void resync (Picture* pic);
double gaussianPeak (double y1, double y2, double y3);
Wave upsampleLanczos (Wave orig, int factor, double middle=1500, int a=3);
std::tuple<bool,double,double> findMelody (Wave, Melody, double);
std::vector<int> readFSK (DSPworker*, double, double, double, size_t);
SSTVMode readVIS (DSPworker*, double fshift=0);
Wave upsampleLanczos (Wave orig, int factor, size_t a=3);
Wave Hann (std::size_t);
Wave Blackmann (std::size_t);
Wave Rect (std::size_t);
Wave Gauss (std::size_t);
Wave deriv (Wave);
Wave peaks (Wave, int);
Wave derivPeaks (Wave, int);
Wave peaks (Wave, size_t);
Wave derivPeaks (Wave, size_t);
Wave rms (Wave, int);
void runTest(const char*);
double complexMag (fftw_complex coeff);
guint8 freq2lum(double);
void renderPixbuf(Picture* pic);
void printWave(Wave, double);
void evt_AbortRx ();

Wyświetl plik

@ -49,10 +49,12 @@ DSPworker::DSPworker() : Mutex(), please_stop_(false) {
fft_plan_big_ = fftw_plan_dft_1d(FFT_LEN_BIG, fft_inbuf_, fft_outbuf_, FFTW_FORWARD, FFTW_ESTIMATE);
cirbuf_tail_ = 0;
cirbuf_head_ = 0;
cirbuf_head_ = MOMENT_LEN/2;
cirbuf_fill_count_ = 0;
is_open_ = false;
fshift_ = 0;
t_ = 0;
sync_window_ = WINDOW_HANN511;
}
@ -60,23 +62,24 @@ void DSPworker::openAudioFile (std::string fname) {
if (!is_open_) {
fprintf (stderr,"Open '%s'\n", fname.c_str()) ;
fprintf (stderr,"open '%s'\n", fname.c_str()) ;
file_ = SndfileHandle(fname.c_str()) ;
if (file_.error()) {
fprintf(stderr,"(sndfile) %s\n", file_.strError());
} else {
fprintf (stderr," Sample rate : %d\n", file_.samplerate ()) ;
fprintf (stderr," Channels : %d\n", file_.channels ()) ;
fprintf (stderr," opened @ %d Hz, %d ch\n", file_.samplerate(), file_.channels()) ;
samplerate_ = file_.samplerate();
stream_type_ = STREAM_TYPE_FILE;
t_ = 0;
num_chans_ = file_.channels();
read_buffer_ = new short [READ_CHUNK_LEN * num_chans_];
is_open_ = true;
readMore();
/* RAII takes care of destroying SndfileHandle object. */
}
}
}
@ -84,6 +87,8 @@ void DSPworker::openAudioFile (std::string fname) {
void DSPworker::openPortAudio () {
if (!is_open_) {
fprintf(stderr,"open PortAudio\n");
Pa_Initialize();
PaStreamParameters inputParameters;
@ -106,24 +111,33 @@ void DSPworker::openPortAudio () {
NULL, /* no callback, use blocking API */
NULL ); /* no callback, so no callback userData */
if (err == paNoError)
printf("opened %s\n",devinfo->name);
err = Pa_StartStream( pa_stream_ );
if (err == paNoError)
printf("stream started\n");
if (err == paNoError) {
err = Pa_StartStream( pa_stream_ );
const PaStreamInfo *streaminfo;
streaminfo = Pa_GetStreamInfo(pa_stream_);
samplerate_ = streaminfo->sampleRate;
printf("%f\n",samplerate_);
const PaStreamInfo *streaminfo;
streaminfo = Pa_GetStreamInfo(pa_stream_);
samplerate_ = streaminfo->sampleRate;
fprintf(stderr," opened '%s' @ %.1f\n",devinfo->name,samplerate_);
stream_type_ = STREAM_TYPE_PA;
stream_type_ = STREAM_TYPE_PA;
num_chans_ = 1;
read_buffer_ = new short [READ_CHUNK_LEN * num_chans_];
is_open_ = true;
readMore();
if (err == paNoError) {
is_open_ = true;
readMore();
} else {
fprintf(stderr," error at Pa_StartStream\n");
}
} else {
fprintf(stderr," error\n");
}
}
}
void DSPworker::set_fshift(double fshift) {
fshift_ = fshift;
}
int DSPworker::freq2bin (double freq, int fft_len) {
return (freq / samplerate_ * fft_len);
@ -137,28 +151,30 @@ double DSPworker::get_t() {
return t_;
}
bool DSPworker::isLive() {
return (stream_type_ == STREAM_TYPE_PA);
}
void DSPworker::readMore () {
int numchans = file_.channels();
short read_buffer[READ_CHUNK_LEN * numchans];
sf_count_t samplesread = 0;
if (is_open_) {
if (stream_type_ == STREAM_TYPE_FILE) {
samplesread = file_.readf(read_buffer, READ_CHUNK_LEN);
samplesread = file_.readf(read_buffer_, READ_CHUNK_LEN);
if (samplesread < READ_CHUNK_LEN)
is_open_ = false;
if (numchans > 1) {
if (num_chans_ > 1) {
for (int i=0; i<READ_CHUNK_LEN; i++) {
read_buffer[i] = read_buffer[i*numchans];
read_buffer_[i] = read_buffer_[i*num_chans_];
}
}
} else if (stream_type_ == STREAM_TYPE_PA) {
samplesread = READ_CHUNK_LEN;
int err = Pa_ReadStream( pa_stream_, read_buffer, READ_CHUNK_LEN );
int err = Pa_ReadStream( pa_stream_, read_buffer_, READ_CHUNK_LEN );
if (err != paNoError)
is_open_ = false;
}
@ -166,11 +182,11 @@ void DSPworker::readMore () {
int cirbuf_fits = std::min(CIRBUF_LEN - cirbuf_head_, (int)samplesread);
memcpy(&cirbuf_[cirbuf_head_], read_buffer, cirbuf_fits * sizeof(read_buffer[0]));
memcpy(&cirbuf_[cirbuf_head_], read_buffer_, cirbuf_fits * sizeof(read_buffer_[0]));
// wrapped around
if (samplesread > cirbuf_fits) {
memcpy(&cirbuf_[0], &read_buffer[cirbuf_fits], (samplesread - cirbuf_fits) * sizeof(read_buffer[0]));
memcpy(&cirbuf_[0], &read_buffer_[cirbuf_fits], (samplesread - cirbuf_fits) * sizeof(read_buffer_[0]));
}
// mirror
@ -216,9 +232,9 @@ void DSPworker::windowedMoment (WindowType win_type, fftw_complex *result) {
//double if_phi = 0;
for (int i = 0; i < MOMENT_LEN; i++) {
int win_i = i - MOMENT_LEN/2 + window_[win_type].size()/2 ;
size_t win_i = i - MOMENT_LEN/2 + window_[win_type].size()/2 ;
if (win_i >= 0 && win_i < window_[win_type].size()) {
if (win_i < window_[win_type].size()) {
double a;
//fftw_complex mixed;
a = cirbuf_[cirbuf_tail_ + i] * window_[win_type][win_i];
@ -257,24 +273,21 @@ double DSPworker::peakFreq (double minf, double maxf, WindowType wintype) {
}
double result = peakBin + gaussianPeak(Mag[peakBin-1], Mag[peakBin], Mag[peakBin+1]);
/*double y1 = Mag[peakBin-1], y2 = Mag[peakBin], y3 = Mag[peakBin+1];
double result = peakBin + (y3 - y1) / (2 * (2*y2 - y3 - y1));*/
// In Hertz
result = result / fft_len * samplerate_;
result = result / fft_len * samplerate_ + fshift_;
// cheb47 @ 44100 can't resolve <1800 Hz
if (result < 1800 && wintype == WINDOW_CHEB47)
// cheb47 @ 44100 can't resolve <1700 Hz nominal
if (result < 1700 && wintype == WINDOW_CHEB47)
result = peakFreq (minf, maxf, WINDOW_HANN95);
return result;
}
Wave DSPworker::bandPowerPerHz(std::vector<std::vector<double> > bands) {
Wave DSPworker::bandPowerPerHz(std::vector<std::vector<double> > bands, WindowType wintype) {
int fft_len = FFT_LEN_BIG;
WindowType wintype = WINDOW_HANN2047;
int fft_len = (window_[wintype].size() <= FFT_LEN_SMALL ? FFT_LEN_SMALL : FFT_LEN_BIG);
fftw_complex windowed[window_[wintype].size()];
windowedMoment(wintype, windowed);
@ -287,7 +300,7 @@ Wave DSPworker::bandPowerPerHz(std::vector<std::vector<double> > bands) {
double P = 0;
double binwidth = 1.0 * samplerate_ / fft_len;
int nbins = 0;
for (int i = freq2bin(band[0], fft_len); i <= freq2bin(band[1], fft_len); i++) {
for (int i = freq2bin(band[0]+fshift_, fft_len); i <= freq2bin(band[1]+fshift_, fft_len); i++) {
P += pow(complexMag(fft_outbuf_[i]), 2);
nbins++;
}
@ -300,23 +313,59 @@ Wave DSPworker::bandPowerPerHz(std::vector<std::vector<double> > bands) {
WindowType DSPworker::bestWindowFor(SSTVMode Mode, double SNR) {
WindowType WinType;
double samplesInPixel = 1.0 * samplerate_ * ModeSpec[Mode].tScan / ModeSpec[Mode].ScanPixels;
//double samplesInPixel = 1.0 * samplerate_ * ModeSpec[Mode].tScan / ModeSpec[Mode].ScanPixels;
if (SNR >= 23 && Mode != MODE_PD180 && Mode != MODE_SDX) WinType = WINDOW_CHEB47;
else if (SNR >= 12) WinType = WINDOW_HANN95;
else if (SNR >= 8) WinType = WINDOW_HANN127;
else if (SNR >= 5) WinType = WINDOW_HANN255;
else if (SNR >= 4) WinType = WINDOW_HANN511;
else if (SNR >= -7) WinType = WINDOW_HANN1023;
else if (SNR >= -3) WinType = WINDOW_HANN1023;
else WinType = WINDOW_HANN2047;
return WinType;
}
double DSPworker::videoSNR () {
if (t_ >= next_snr_time_) {
std::vector<double> bands = bandPowerPerHz({{200,1000}, {1500,2300}, {2700, 2900}});
double Pvideo_plus_noise = bands[1];
double Pnoise_only = (bands[0] + bands[2]) / 2;
double Psignal = Pvideo_plus_noise - Pnoise_only;
SNR_ = ((Pnoise_only == 0 || Psignal / Pnoise_only < .01) ? -20 : 10 * log10(Psignal / Pnoise_only));
next_snr_time_ = t_ + 50e-3;
}
return SNR_;
}
bool DSPworker::hasSync () {
std::vector<double> bands = bandPowerPerHz({{1150,1250}, {1500,2300}}, sync_window_);
return (bands[0] > 2 * bands[1]);
}
double DSPworker::lum (SSTVMode mode, bool is_adaptive) {
WindowType win_type;
if (is_adaptive) win_type = bestWindowFor(mode, videoSNR());
else win_type = bestWindowFor(mode);
double freq = peakFreq(1500, 2300, win_type);
return fclip((freq - 1500.0) / (2300.0-1500.0));
}
// param: y values around peak
// return: peak x position (-1 .. 1)
double gaussianPeak (double y1, double y2, double y3) {
return ((y3 - y1) / (2 * (2*y2 - y3 - y1)));
if (2*y2 - y3 - y1 == 0) {
return 0;
} else {
return ((y3 - y1) / (2 * (2*y2 - y3 - y1)));
}
}
/*WindowType DSPworker::bestWindowFor(SSTVMode Mode) {
return bestWindowFor(Mode, 20);
@ -389,15 +438,11 @@ double complexMag (fftw_complex coeff) {
return sqrt(pow(coeff[0],2) + pow(coeff[1],2));
}
guint8 freq2lum (double freq) {
return clip((freq - 1500.0) / (2300.0-1500.0) * 255 + .5);
}
double sinc (double x) {
return (x == 0 ? 1 : sin(M_PI*x) / (M_PI*x));
}
Wave upsampleLanczos(Wave orig, int factor, double middle_freq, int a) {
Wave upsampleLanczos(Wave orig, int factor, size_t a) {
Wave result(orig.size()*factor);
int kernel_len = factor*a*2 + 1;
@ -410,40 +455,37 @@ Wave upsampleLanczos(Wave orig, int factor, double middle_freq, int a) {
}
// convolution
for (int i=-a; i<int(orig.size()+a); i++) {
for (int orig_i=-a; orig_i<int(orig.size()+a); orig_i++) {
double orig_sample;
if (i < 0)
if (orig_i < 0)
orig_sample = orig[0];
else if (i > orig.size()-1)
else if (orig_i > int(orig.size()-1))
orig_sample = orig[orig.size()-1];
else
orig_sample = orig[i];
orig_sample = orig[orig_i];
if (orig_sample != 0) {
for (int kernel_idx=0; kernel_idx<kernel_len; kernel_idx++) {
int i_new = i*factor + (kernel_idx-kernel_len/2);
if (i_new >= 0 && i_new <= result.size()-1)
result[i_new] += (orig_sample - middle_freq) * lanczos[kernel_idx];
int i_new = (orig_i+.5)*factor -kernel_len/2 + kernel_idx;
if (i_new >= 0 && i_new <= int(result.size()-1))
result[i_new] += orig_sample * lanczos[kernel_idx];
}
}
}
for (int i=0; i<result.size(); i++)
result[i] += middle_freq;
return result;
}
Wave deriv (Wave wave) {
Wave result;
for (int i=1; i<wave.size(); i++)
for (size_t i=1; i<wave.size(); i++)
result.push_back(wave[i] - wave[i-1]);
return result;
}
std::vector<double> peaks (Wave wave, int n) {
std::vector<double> peaks (Wave wave, size_t n) {
std::vector<std::pair<double,double> > peaks;
for (int i=0; i<wave.size(); i++) {
for (size_t i=0; i<wave.size(); i++) {
double y1 = (i==0 ? wave[0] : wave[i-1]);
double y2 = wave[i];
double y3 = (i==wave.size()-1 ? wave[wave.size()-1] : wave[i+1]);
@ -456,7 +498,7 @@ std::vector<double> peaks (Wave wave, int n) {
});
Wave result;
for (int i=0;i<n && i<peaks.size(); i++)
for (size_t i=0;i<n && i<peaks.size(); i++)
result.push_back(peaks[i].first);
std::sort(result.begin(), result.end());
@ -465,9 +507,9 @@ std::vector<double> peaks (Wave wave, int n) {
}
std::vector<double> derivPeaks (Wave wave, int n) {
std::vector<double> derivPeaks (Wave wave, size_t n) {
std::vector<double> result = peaks(deriv(wave), n);
for (int i=0; i<result.size(); i++) {
for (size_t i=0; i<result.size(); i++) {
result[i] += .5;
}
return result;
@ -479,7 +521,7 @@ Wave rms(Wave orig, int window_width) {
int pool_ptr = 0;
double total = 0;
for (int i=0; i<orig.size(); i++) {
for (size_t i=0; i<orig.size(); i++) {
total -= pool[pool_ptr];
pool[pool_ptr] = pow(orig[i], 2);
total += pool[pool_ptr];
@ -488,3 +530,94 @@ Wave rms(Wave orig, int window_width) {
}
return result;
}
/* returns: vector of bits */
std::vector<int> readFSK (DSPworker *dsp, double baud_rate, double cent_freq, double shift, size_t nbits) {
std::vector<int> result;
double freq_margin = 200;
for (size_t i=0; i<nbits; i++) {
dsp->forward_time(0.5 / baud_rate);
std::vector<double> f = dsp->bandPowerPerHz(
{{cent_freq-shift-freq_margin, cent_freq-shift+freq_margin},
{cent_freq+shift-freq_margin, cent_freq+shift+freq_margin}}
);
result.push_back(f[0] > f[1]);
dsp->forward_time(0.5 / baud_rate);
}
return result;
}
/* pass: wave FM demodulated signal
* melody array of Tones
* (zero frequency to accept any)
* dt sample delta
*
* returns: (bool) did we find it
* (double) at which frequency shift
* (double) started how many seconds before the last sample
*/
std::tuple<bool,double,double> findMelody (Wave wave, Melody melody, double dt) {
bool was_found = true;
int start_at = 0;
double avg_fdiff = 0;
double freq_margin = 25;
double tshift = 0;
double t = melody[melody.size()-1].dur;
std::vector<double> fdiffs;
for (int i=melody.size()-2; i>=0; i--) {
if (melody[i].freq != 0) {
double delta_f_ref = melody[i].freq - melody[melody.size()-1].freq;
double delta_f = wave[wave.size()-1 - (t/dt)] - wave[wave.size()-1];
double fshift = delta_f - delta_f_ref;
was_found &= fabs(fshift) < freq_margin;
}
start_at = wave.size() - (t / dt);
t += melody[i].dur;
}
if (was_found) {
/* refine fshift */
int melody_i = 0;
double next_tone_t = melody[melody_i].dur;
for (size_t i=start_at; i<wave.size(); i++) {
double fref = melody[melody_i].freq;
double fdiff = (wave[i] - fref);
fdiffs.push_back(fdiff);
if ( (i-start_at)*dt >= next_tone_t ) {
melody_i ++;
next_tone_t += melody[melody_i].dur;
}
}
std::sort(fdiffs.begin(), fdiffs.end());
avg_fdiff = fdiffs[fdiffs.size()/2];
/* refine start_at */
Wave subwave(wave.begin()+start_at, wave.end());
Wave edges_rx = derivPeaks(subwave, melody.size()-1);
Wave edges_ref;
double t = 0;
for (size_t i=0; i<melody.size()-1; i++) {
t += melody[i].dur;
edges_ref.push_back(t);
}
tshift = 0;
if (edges_rx.size() == edges_ref.size()) {
for (size_t i=0; i<edges_rx.size(); i++) {
tshift += (edges_rx[i]*dt - edges_ref[i]);
}
tshift = start_at*dt + (tshift / edges_rx.size()) - ((wave.size()-1)*dt);
} else {
// can't refine
tshift = start_at*dt - ((wave.size()-1)*dt);
}
}
return { was_found, avg_fdiff, tshift };
}

Wyświetl plik

@ -1,181 +1,107 @@
#include "common.hh"
#include <cmath>
/* pass: wave FM demodulated signal
* melody array of Tones
* (zero frequency to accept any)
* dt sample delta
*
* returns: (bool) did we find it
* (double) at which frequency shift
* (int) starting at how many dt from beginning of buffer
*/
std::tuple<bool,double,int> findMelody (Wave wave, Melody melody, double dt) {
bool was_found = true;
int start = 0;
double avg_fdiff = 0;
double freq_margin = 25;
double t = melody[melody.size()-1].dur;
std::vector<double> fdiffs;
for (int i=melody.size()-2; i>=0; i--) {
if (melody[i].freq != 0) {
double delta_f_ref = melody[i].freq - melody[melody.size()-1].freq;
double delta_f = wave[wave.size()-1 - (t/dt)] - wave[wave.size()-1];
double fshift = delta_f - delta_f_ref;
was_found &= fabs(fshift) < freq_margin;
}
start = wave.size() - (t / dt);
t += melody[i].dur;
}
if (was_found) {
int melody_i = 0;
double next_tone_t = melody[melody_i].dur;
for (int i=start; i<wave.size(); i++) {
double fref = melody[melody_i].freq;
double fdiff = (wave[i] - fref);
fdiffs.push_back(fdiff);
if ( (i-start)*dt >= next_tone_t ) {
melody_i ++;
next_tone_t += melody[melody_i].dur;
}
}
std::sort(fdiffs.begin(), fdiffs.end());
avg_fdiff = fdiffs[fdiffs.size()/2];
}
return { was_found, avg_fdiff, start };
}
SSTVMode modeFromNextHeader (DSPworker *dsp) {
SSTVMode nextHeader (DSPworker *dsp) {
double dt = 5e-3;
int bitlen = 30e-3 / dt;
int upsample_factor = 10;
SSTVMode mode = MODE_UNKNOWN;
int selmode, ptr=0;
int vis = 0, Parity = 0, ReadPtr = 0;
Wave HedrCirBuf(1100e-3 / dt);
Wave delta_f(1100e-3 / dt);
int ptr_read = 0;
Wave cirbuf_header(1100e-3 / dt);
Wave cirbuf_header_bb(1100e-3 / dt);
Wave freq(1100e-3 / dt);
Wave tone(1100e-3 / dt);
bool gotvis = false;
unsigned Bit[8] = {0}, ParityBit = 0;
Wave freq_bb(1100e-3 / dt);
Melody mmsstv_melody = {
Melody mmsstv_vox = {
Tone(100e-3, 1900), Tone(100e-3, 1500), Tone(100e-3, 1900), Tone(100e-3, 1500),
Tone(100e-3, 2300), Tone(100e-3, 1500), Tone(100e-3, 2300), Tone(100e-3, 1500),
Tone(300e-3, 1900)
};
Melody robot_melody = {
Tone(150e-3, 1900), Tone(150e-3, 1900), Tone(160e-3, 1900), Tone(150e-3, 1900),
Tone(30e-3, 1200), Tone(8*30e-3,0), Tone(30e-3, 1200)
Melody robot_vox = {
Tone(300e-3, 1900), Tone(10e-3, 1200), Tone(300e-3, 1900),
Tone(30e-3, 1200)
};
fprintf(stderr,"Waiting for header\n");
fprintf(stderr,"wait for header\n");
while ( dsp->is_open() ) {
HedrCirBuf[ReadPtr] = dsp->peakFreq(500, 3300, WINDOW_HANN1023);
cirbuf_header[ptr_read] = dsp->peakFreq(500, 3300, WINDOW_HANN1023);
for (int i = 0; i < HedrCirBuf.size(); i++) {
freq[i] = HedrCirBuf[(ReadPtr + 1 + i) % HedrCirBuf.size()];
for (size_t i = 0; i < cirbuf_header.size(); i++) {
freq[i] = cirbuf_header[(ptr_read + 1 + i) % cirbuf_header.size()];
}
double fshift;
std::tuple<bool,double,int> has = findMelody(freq, mmsstv_melody, dt);
std::tuple<bool,double,double> has = findMelody(freq, mmsstv_vox, dt);
if (std::get<0>(has)) {
fshift = std::get<1>(has);
int start = std::get<2>(has);
fprintf(stderr," got MMSSTV header (t=%f s, fshift=%f Hz, start at %d)\n",dsp->get_t(), fshift, start);
double tshift = std::get<2>(has);
fprintf(stderr," got MMSSTV VOX (t=%.03f s, fshift=%+.0f Hz)\n",
dsp->get_t()+tshift, fshift);
Wave powers = rms(deriv(freq), 6);
for (int i=0; i<powers.size(); i++) {
dsp->forward_time(tshift + 8*100e-3 + 300e-3 + 10e-3 + 300e-3 + 30e-3);
}
Wave header_interp = upsampleLanczos(freq, upsample_factor, 1900);
std::vector<double> peaks_pos = derivPeaks(header_interp, 8);
mode = readVIS(dsp, fshift);
printWave(freq, dt);
exit(0);
}
if (false) {
double fshift = ((delta_f[0] - 1900) + delta_f[4*bitlen] + delta_f[6*bitlen] + delta_f[8*bitlen] +
delta_f[10*bitlen]) / 5.0;
printf(" got robot header: t=%f, fshift=%+.1f Hz\n",dsp->get_t(),fshift);
// hi-res zero-crossing search
Wave abs_f(delta_f.size());
abs_f[0] = delta_f[0];
for (int i=1; i<abs_f.size(); i++)
abs_f[i] += delta_f[0] + delta_f[i];
Wave header_interp = upsampleLanczos(abs_f, upsample_factor);
int n_zc = 0;
double t_zc1=0, t_zc2=0;
double s=0,s0=0;
for (int i=0; i<header_interp.size(); i++) {
s = header_interp[i] - fshift - (1200.0+(1900.0-1200.0)/2.0);
double t = dsp->get_t() - (header_interp.size()-1-i)*dt/upsample_factor;
//printf("%f,%f\n",t,s);
if (i*dt/upsample_factor>15e-3 && s * s0 < 0) {
if (n_zc == 1) {
t_zc1 = t - dt + ((-s0)/(s-s0))*dt/upsample_factor;
}
if (n_zc > 1 && t > t_zc1+270e-3 ) {
t_zc2 = t - dt + ((-s0)/(s-s0))*dt/upsample_factor;
break;
}
n_zc++;
}
s0 = s;
}
printf (" n_zc=%d, t_zc1=%f, t_zc2=%fi (dur = %.05f ms, speed = %.5f)\n",n_zc,t_zc1,t_zc2,
(t_zc2-t_zc1)*1e3,
300e-3 / (t_zc2 - t_zc1)
);
int parity_rx=0;
for (int k=0; k<8; k++) {
if (abs_f[(13+k)*bitlen]+fshift < 1200) {
vis |= (1 << k);
parity_rx ++;
}
}
vis &= 0x7f;
printf(" got VIS: %dd (%02Xh) @ %+f Hz\n", vis, vis, fshift);
if (vis2mode.find(vis) == vis2mode.end()) {
printf(" Unknown VIS\n");
gotvis = false;
} else {
if ((parity_rx % 2) != ModeSpec[vis2mode[vis]].VISParity) {
printf(" Parity fail\n");
gotvis = false;
} else {
printf(" %s\n",ModeSpec[vis2mode[vis]].Name.c_str());
double start_of_video = t_zc2 + 10 * 30e-3;
dsp->forward_to_time(start_of_video);
break;
}
if (mode != MODE_UNKNOWN) {
dsp->set_fshift(fshift);
dsp->forward_time(30e-3);
break;
}
}
ReadPtr = (ReadPtr+1) % HedrCirBuf.size();
has = findMelody(freq, robot_vox, dt);
if (std::get<0>(has)) {
fshift = std::get<1>(has);
double tshift = std::get<2>(has);
fprintf(stderr," got Robot header (t=%f s, fshift=%+.1f Hz)\n",dsp->get_t(),fshift);
dsp->forward_time(tshift + 300e-3 + 10e-3 + 300e-3 + 30e-3);
mode = readVIS(dsp, fshift);
if (mode != MODE_UNKNOWN) {
dsp->set_fshift(fshift);
dsp->forward_time(30e-3);
break;
}
}
ptr_read = (ptr_read+1) % cirbuf_header.size();
dsp->forward_time(dt);
}
return vis2mode[vis];
return mode;
}
SSTVMode readVIS(DSPworker* dsp, double fshift) {
int vis = 0;
SSTVMode mode = MODE_UNKNOWN;
std::vector<int> bits = readFSK(dsp, 33.333, 1200+fshift, 100, 8);
int parity_rx=0;
for (int i=0; i<8; i++) {
vis |= (bits[i] << i);
parity_rx += bits[i];
}
vis &= 0x7F;
if (vis2mode.find(vis) == vis2mode.end()) {
fprintf(stderr,"(unknown mode %dd=%02Xh)\n",vis,vis);
} else {
if ((parity_rx % 2) != ModeSpec[vis2mode[vis]].VISParity) {
fprintf(stderr,"(parity fail)\n");
} else {
fprintf(stderr," got VIS: %dd / %02Xh (%s)", vis, vis,
ModeSpec[vis2mode[vis]].Name.c_str());
mode = vis2mode[vis];
}
}
return mode;
}

Wyświetl plik

@ -21,13 +21,14 @@ int main(int argc, char *argv[]) {
break;
}
upsampleLanczos({0},10);
if (!dsp.is_open())
dsp.openPortAudio();
GetVideo(modeFromNextHeader(&dsp), &dsp);
SSTVMode mode = nextHeader(&dsp);
if (mode != MODE_UNKNOWN) {
rxVideo(mode, &dsp);
}
//SlowGUI gui = SlowGUI();
return 0;

Wyświetl plik

@ -1,93 +1,68 @@
#include "common.hh"
void findSyncHough (SSTVMode Mode, std::vector<bool> has_sync) {
// TODO: middle point of sync pulse
void resync (Picture* pic) {
int line_width = ModeSpec[pic->mode].tLine / pic->sync_dt;
}
double findSyncAutocorr (SSTVMode Mode, std::vector<bool> has_sync) {
int line_width = ModeSpec[Mode].NumLines;
std::vector<int> jakauma;
/* speed */
std::vector<int> histogram;
int peak_speed = 0;
int peak_speed_val = 0;
int peak_pos = 0;
double min_spd = 0.998;
double max_spd = 1.002;
double spd_step = 0.0002;
double spd_step = 0.00005;
for (double speed = min_spd; speed <= max_spd; speed += spd_step) {
std::vector<int> acc(line_width);
int peak_x = 0;
for (int i=0; i<has_sync.size(); i++) {
for (size_t i=1; i<pic->sync_signal.size(); i++) {
int x = int(i / speed + .5) % line_width;
acc[x] += has_sync[i];
acc[x] += pic->sync_signal[i] && !pic->sync_signal[i-1];
if (acc[x] > acc[peak_x]) {
peak_x = x;
}
}
jakauma.push_back(acc[peak_x]);
histogram.push_back(acc[peak_x]);
if (acc[peak_x] > peak_speed_val) {
peak_speed = jakauma.size()-1;
peak_speed = histogram.size()-1;
peak_speed_val = acc[peak_x];
peak_pos = peak_x;
}
printf("(%.5f=%.0f) %d\n",1.0/speed,44100.0/speed,acc[peak_x]);
/*for (int x=0; x<acc.size(); x++) {
printf("%4d ",acc[x]);
}
printf("\n");*/
}
double peak_refined = peak_speed + gaussianPeak(jakauma[peak_speed-1], jakauma[peak_speed], jakauma[peak_speed+1]);
double peak_refined = peak_speed +
gaussianPeak(histogram[peak_speed-1], histogram[peak_speed], histogram[peak_speed+1]);
double spd = 1.0/(min_spd + peak_refined*spd_step);
printf("%.5f\n",spd);
printf("--> %.1f\n",44100*spd);
printf("pos = %d (%.1f ms)\n",peak_pos,1.0*peak_pos/line_width*ModeSpec[Mode].tLine*1000);
return spd;
}
void findSyncRansac(SSTVMode Mode, std::vector<bool> has_sync) {
int line_width = ModeSpec[Mode].NumLines;//ModeSpec[Mode].tLine / ModeSpec[Mode].tSync * 4;
std::vector<Point> sync_pixels;
for (int y=0; y<ModeSpec[Mode].NumLines; y++) {
for (int x=0; x<line_width; x++) {
if (y+x>0 && has_sync[y*line_width + x] && !has_sync[y*line_width + x - 1]) {
sync_pixels.push_back(Point(x,y));
printf("%d,%d\n",x,y);
}
}
/* align */
size_t peak_align = 0;
std::vector<int> acc(line_width);
for (size_t i=1;i<pic->sync_signal.size(); i++) {
int x = int(i * spd + .5) % line_width;
acc[x] += pic->sync_signal[i] && !pic->sync_signal[i-1];
if (acc[x] > acc[peak_align])
peak_align = x;
}
std::pair<Point, Point > best_line;
double best_dist = -1;
int it_num = 0;
while (++it_num < 300) {
double peak_align_refined = peak_align +
(peak_align == 0 || peak_align == acc.size()-1 ? 0 :
gaussianPeak(acc[peak_align-1], acc[peak_align], acc[peak_align+1]));
std::random_shuffle(sync_pixels.begin(), sync_pixels.end());
if (ModeSpec[pic->mode].SyncOrder == SYNC_SCOTTIE)
peak_align_refined = peak_align_refined -
(line_width*(ModeSpec[pic->mode].tSync + ModeSpec[pic->mode].tSep*2 + ModeSpec[pic->mode].tScan*2)/ModeSpec[pic->mode].tLine);
std::pair<Point, Point > test_line = {sync_pixels[0], sync_pixels[1]};
int x0 = test_line.first.x;
int y0 = test_line.first.y;
int x1 = test_line.second.x;
int y1 = test_line.second.y;
double total_sq_dist = 0;
int total_good = 0;
printf("%f\n",peak_align_refined);
for(Point pixel : sync_pixels) {
int x = pixel.x;
int y = pixel.y;
// Point distance to line
double d = 1.0 * ((y0-y1)*x + (x1-x0)*y + (x0*y1 - x1*y0)) / sqrt(pow(x1-x0,2) + pow(y1-y0,2));
if (peak_align_refined > line_width/2.0)
peak_align_refined -= line_width;
if (fabs(d) < 6 || fabs(d-line_width) < 6) {
total_good ++;//+= sqrt(fabs(d));
}
fprintf(stderr,"%.5f\n",spd);
fprintf(stderr, "--> %.1f\n",44100*spd);
fprintf(stderr,"align = %f = %.3f %% (%.3f ms)\n",
peak_align_refined,
1.0*peak_align_refined/line_width*100,
1.0*peak_align_refined/line_width*ModeSpec[pic->mode].tLine*1000);
}
if (best_dist < 0 || total_good > best_dist) {
best_line = test_line;
best_dist = total_good;
printf("(it. %d) for (%d,%d) (%d,%d) total_sq_dist=%f, total_good=%d\n",it_num,x0,y0,x1,y1,total_sq_dist,total_good);
}
}
pic->speed = spd;
pic->starts_at = 1.0*peak_align_refined/line_width*ModeSpec[pic->mode].tLine;
}

53
src/tests.cc 100644
Wyświetl plik

@ -0,0 +1,53 @@
#include "common.hh"
void printWave (Wave wave, double dt) {
for (int i=0;i<wave.size();i++)
printf("%lf,%lf\n",i*dt,wave[i]);
}
double sum (std::vector<double> nums) {
double result = 0;
for (double d : nums)
result += d;
return result;
}
double mean (std::vector<double> nums) {
return sum(nums) / nums.size();
}
double sdev (std::vector<double> nums) {
double result = 0;
double _mean = mean(nums);
for (double d : nums)
result += pow(d - _mean, 2);
result = sqrt(result / nums.size());
return result;
}
void runTest(const char *sweepsound) {
DSPworker dsp;
double tone_len = 0.3;
dsp.openAudioFile(sweepsound);
std::map<double, std::vector<double> > errors;
while(dsp.is_open()) {
double f = dsp.peakFreq(1200,2500,WINDOW_HANN63);
double lum_should_be = int(dsp.get_t()/tone_len) % 256;
double f_should_be = 1500 + lum_should_be / 255.0 * (2300-1500);
//printf("f should be %.0f, is %.0f (error %.1f)\n",f_should_be,f,f - f_should_be);
errors[f_should_be].push_back((f - f_should_be) );
dsp.forward();
}
typedef std::map<double, std::vector<double> >::iterator it_type;
for(it_type iterator = errors.begin(); iterator != errors.end(); iterator++) {
double k = iterator->first;
std::vector<double> errs = iterator->second;
printf("%f,%f\n",k,sdev(errs));
}
}

Wyświetl plik

@ -1,60 +1,104 @@
#include "common.hh"
typedef struct {
Point pt;
int Channel;
double Time;
} PixelSample;
void renderPixbuf(Picture *pic) {
int upsample_factor = 4;
std::vector<PixelSample> pixel_grid = getPixelSamplingPoints(pic->mode);
guint8 lum[800][800][3];
Glib::RefPtr<Gdk::Pixbuf> pixbuf_rx;
pixbuf_rx = Gdk::Pixbuf::create(Gdk::COLORSPACE_RGB, false, 8, ModeSpec[pic->mode].ScanPixels, ModeSpec[pic->mode].NumLines);
pixbuf_rx->fill(0x000000ff);
// Map to RGB & store in pixbuf
void toPixbufRGB(guint8 Image[800][800][3], Glib::RefPtr<Gdk::Pixbuf> pixbuf, SSTVMode Mode) {
guint8 *p;
guint8 *pixels;
pixels = pixbuf->get_pixels();
int rowstride = pixbuf->get_rowstride();
for (int x = 0; x < ModeSpec[Mode].ScanPixels; x++) {
for (int y = 0; y < ModeSpec[Mode].NumLines; y++) {
pixels = pixbuf_rx->get_pixels();
int rowstride = pixbuf_rx->get_rowstride();
Wave signal_up = upsampleLanczos(pic->video_signal, upsample_factor, 3);
for (size_t pixel_idx = 0; pixel_idx <= pixel_grid.size(); pixel_idx ++) {
PixelSample px = pixel_grid[pixel_idx];
double signal_t = (px.Time/pic->speed + pic->starts_at) / pic->video_dt * upsample_factor;
double val;
if (signal_t < 0 || signal_t >= signal_up.size()-1) {
val = 0;
} else {
double d = signal_t - int(signal_t);
val = (1-d) * signal_up[signal_t] +
d * signal_up[signal_t+1];
}
int x = pixel_grid[pixel_idx].pt.x;
int y = pixel_grid[pixel_idx].pt.y;
int ch = pixel_grid[pixel_idx].Channel;
lum[x][y][ch] = clip(val*255);
}
if (ModeSpec[pic->mode].SubSampling == SUBSAMP_420_YUYV) {
for (size_t x=0; x < ModeSpec[pic->mode].ScanPixels; x++) {
std::vector<double> column_u, column_u_filtered;
std::vector<double> column_v, column_v_filtered;
for (size_t y=0; y < ModeSpec[pic->mode].NumLines; y+=2) {
column_u.push_back(lum[x][y][1]);
column_v.push_back(lum[x][y][2]);
}
column_u_filtered = upsampleLanczos(column_u, 2, 2);
column_v_filtered = upsampleLanczos(column_v, 2, 2);
for (size_t y=0; y < ModeSpec[pic->mode].NumLines; y++) {
lum[x][y][1] = column_u_filtered[y];
lum[x][y][2] = column_v_filtered[y];
}
}
}
for (size_t x = 0; x < ModeSpec[pic->mode].ScanPixels; x++) {
for (size_t y = 0; y < ModeSpec[pic->mode].NumLines; y++) {
p = pixels + y * rowstride + x * 3;
switch(ModeSpec[Mode].ColorEnc) {
switch(ModeSpec[pic->mode].ColorEnc) {
case COLOR_RGB:
p[0] = Image[x][y][0];
p[1] = Image[x][y][1];
p[2] = Image[x][y][2];
p[0] = lum[x][y][0];
p[1] = lum[x][y][1];
p[2] = lum[x][y][2];
break;
case COLOR_GBR:
p[0] = Image[x][y][2];
p[1] = Image[x][y][0];
p[2] = Image[x][y][1];
p[0] = lum[x][y][2];
p[1] = lum[x][y][0];
p[2] = lum[x][y][1];
break;
case COLOR_YUV:
// TODO chroma filtering
p[0] = clip((100 * Image[x][y][0] + 140 * Image[x][y][1] - 17850) / 100.0);
p[1] = clip((100 * Image[x][y][0] - 71 * Image[x][y][1] - 33 *
Image[x][y][2] + 13260) / 100.0);
p[2] = clip((100 * Image[x][y][0] + 178 * Image[x][y][2] - 22695) / 100.0);
p[0] = clip((100 * lum[x][y][0] + 140 * lum[x][y][1] - 17850) / 100.0);
p[1] = clip((100 * lum[x][y][0] - 71 * lum[x][y][1] - 33 *
lum[x][y][2] + 13260) / 100.0);
p[2] = clip((100 * lum[x][y][0] + 178 * lum[x][y][2] - 22695) / 100.0);
break;
case COLOR_MONO:
p[0] = p[1] = p[2] = Image[x][y][0];
p[0] = p[1] = p[2] = lum[x][y][0];
break;
}
}
}
pixbuf_rx->save("testi.png", "png");
}
// Time instants for all pixels
std::vector<PixelSample> getPixelSamplingPoints(SSTVMode Mode) {
_ModeSpec s = ModeSpec[Mode];
std::vector<PixelSample> PixelGrid;
for (int y=0; y<s.NumLines; y++) {
for (int x=0; x<s.ScanPixels; x++) {
for (int Chan=0; Chan < (s.ColorEnc == COLOR_MONO ? 1 : 3); Chan++) {
std::vector<PixelSample> getPixelSamplingPoints(SSTVMode mode) {
_ModeSpec s = ModeSpec[mode];
std::vector<PixelSample> pixel_grid;
for (size_t y=0; y<s.NumLines; y++) {
for (size_t x=0; x<s.ScanPixels; x++) {
for (size_t Chan=0; Chan < (s.ColorEnc == COLOR_MONO ? 1 : 3); Chan++) {
PixelSample px;
px.pt = Point(x,y);
px.Channel = Chan;
@ -142,16 +186,16 @@ std::vector<PixelSample> getPixelSamplingPoints(SSTVMode Mode) {
}
break;
}
PixelGrid.push_back(px);
pixel_grid.push_back(px);
}
}
}
std::sort(PixelGrid.begin(), PixelGrid.end(), [](PixelSample a, PixelSample b) {
std::sort(pixel_grid.begin(), pixel_grid.end(), [](PixelSample a, PixelSample b) {
return a.Time < b.Time;
});
return PixelGrid;
return pixel_grid;
}
/* Demodulate the video signal & store all kinds of stuff for later stages
@ -161,127 +205,73 @@ std::vector<PixelSample> getPixelSamplingPoints(SSTVMode Mode) {
* Redraw: false = Apply windowing and FFT to the signal, true = Redraw from cached FFT data
* returns: true when finished, false when aborted
*/
bool GetVideo(SSTVMode Mode, DSPworker* dsp) {
bool rxVideo(SSTVMode mode, DSPworker* dsp) {
printf("receive %s\n",ModeSpec[Mode].Name.c_str());
printf("receive %s\n",ModeSpec[mode].Name.c_str());
_ModeSpec s = ModeSpec[Mode];
guint8 Image[800][800][3];
guint8 Imagesnr[800][800][3];
_ModeSpec s = ModeSpec[mode];
Picture pic(mode);
Glib::RefPtr<Gtk::Application> app = Gtk::Application::create("com.windytan.slowrx");
Glib::RefPtr<Gdk::Pixbuf> pixbuf_rx;
pixbuf_rx = Gdk::Pixbuf::create(Gdk::COLORSPACE_RGB, false, 8, s.ScanPixels, s.NumLines);
pixbuf_rx->fill(0x000000ff);
Glib::RefPtr<Gdk::Pixbuf> pixbuf_snr;
pixbuf_snr = Gdk::Pixbuf::create(Gdk::COLORSPACE_RGB, false, 8, s.ScanPixels, s.NumLines);
pixbuf_snr->fill(0x000000ff);
double next_sync_sample_time = 0;
std::vector<bool> has_sync;
double next_video_sample_time = 0;
/*g_object_unref(pixbuf_disp);
pixbuf_disp = gdk_pixbuf_scale_simple(pixbuf_rx, 500,
500.0/ModeSpec[Mode].ImgWidth * ModeSpec[Mode].NumLines * ModeSpec[Mode].LineHeight, GDK_INTERP_BILINEAR);
*/
//gtk_image_set_from_pixbuf(GTK_IMAGE(gui.image_rx), pixbuf_disp);
double t_total = (s.SyncOrder == SYNC_SCOTTIE ? s.tSync : 0) + s.NumLines * s.tLine;
size_t idx = 0;
/*SyncTargetBin = GetBin(1200+CurrentPic.HedrShift, FFTLen);
Abort = false;
SyncSampleNum = 0;*/
for (double t=0; t < t_total && dsp->is_open(); t += dsp->forward()) {
// Loop through signal
std::vector<PixelSample> PixelGrid = getPixelSamplingPoints(Mode);
double t = 0;
for (int PixelIdx = 0; PixelIdx < PixelGrid.size(); PixelIdx++) {
if (t >= next_sync_sample_time) {
pic.sync_signal.push_back(dsp->hasSync());
while (t < PixelGrid[PixelIdx].Time && dsp->is_open()) {
t += dsp->forward();
next_sync_sample_time += pic.sync_dt;
}
/*** Store the sync band for later adjustments ***/
bool is_adaptive = true;
if (dsp->get_t() >= next_sync_sample_time) {
if ( t >= next_video_sample_time ) {
int line_width = ModeSpec[Mode].NumLines;
pic.video_signal.push_back(dsp->lum(pic.mode, is_adaptive));
std::vector<double> bands = dsp->bandPowerPerHz({{1150,1250}, {1500,2300}});
// If there is more than twice the amount of power per Hz in the
// sync band than in the video band, we have a sync signal here
has_sync.push_back(bands[0] > 2 * bands[1]);
next_sync_sample_time += ModeSpec[Mode].tLine / line_width;
if ((idx+1) % 1000 == 0) {
size_t prog_width = 50;
fprintf(stderr," [");
double prog = t / t_total;
size_t prog_points = prog * prog_width + .5;
for (size_t i=0;i<prog_points;i++) {
fprintf(stderr,"=");
}
for (size_t i=prog_points;i<prog_width;i++) {
fprintf(stderr," ");
}
fprintf(stderr,"] %.1f %%\r",prog*100);
}
if (dsp->isLive() && (idx+1) % 10000 == 0) {
resync(&pic);
renderPixbuf(&pic);
}
next_video_sample_time += pic.video_dt;
idx++;
}
/*** Estimate SNR ***/
double SNR;
bool Adaptive = true;
if (PixelIdx == 0 || (Adaptive && PixelGrid[PixelIdx].pt.x == s.ScanPixels/2)) {
std::vector<double> bands = dsp->bandPowerPerHz({{300,1100}, {1500,2300}, {2500, 2700}});
double Pvideo_plus_noise = bands[1];
double Pnoise_only = (bands[0] + bands[2]) / 2;
double Psignal = Pvideo_plus_noise - Pnoise_only;
SNR = ((Pnoise_only == 0 || Psignal / Pnoise_only < .01) ? -20 : 10 * log10(Psignal / Pnoise_only));
}
/*** FM demodulation ***/
//PrevFreq = Freq;
// Adapt window size to SNR
WindowType WinType;
if (Adaptive) WinType = dsp->bestWindowFor(Mode, SNR);
else WinType = dsp->bestWindowFor(Mode);
double Freq = dsp->peakFreq(1500, 2300, WinType);
// Linear interpolation of (chronologically) intermediate frequencies, for redrawing
//InterpFreq = PrevFreq + (Freq-PrevFreq) * ... // TODO!
// Calculate luminency & store for later use
guint8 Lum = freq2lum(Freq);
//measured.push_back({t, Lum});
//StoredLum[SampleNum] = clip((Freq - (1500 + CurrentPic.HedrShift)) / 3.1372549);
int x = PixelGrid[PixelIdx].pt.x;
int y = PixelGrid[PixelIdx].pt.y;
int Channel = PixelGrid[PixelIdx].Channel;
// Store pixel
Image[x][y][Channel] = Lum;//StoredLum[SampleNum];
Imagesnr[x][y][Channel] = WinType;//StoredLum[SampleNum];
}
/* sync */
findSyncAutocorr(Mode, has_sync);
toPixbufRGB(Image, pixbuf_rx, Mode);
toPixbufRGB(Imagesnr, pixbuf_snr, Mode);
/*if (!Redraw || y % 5 == 0 || PixelIdx == PixelGrid.size()-1) {
resync(&pic);
renderPixbuf(&pic);
/*if (!Redraw || y % 5 == 0 || PixelIdx == pixel_grid.size()-1) {
// Scale and update image
g_object_unref(pixbuf_disp);
pixbuf_disp = gdk_pixbuf_scale_simple(pixbuf_rx, 500,
500.0/ModeSpec[Mode].ImgWidth * ModeSpec[Mode].NumLines * ModeSpec[Mode].LineHeight, GDK_INTERP_BILINEAR);
500.0/ModeSpec[mode].ImgWidth * ModeSpec[mode].NumLines * ModeSpec[mode].LineHeight, GDK_INTERP_BILINEAR);
gtk_image_set_from_pixbuf(GTK_IMAGE(gui.image_rx), pixbuf_disp);
}*/
pixbuf_rx->save("testi.png", "png");
pixbuf_snr->save("snr.png", "png");
fprintf(stderr, "\n");
return true;