dev
Oona Räisänen 2015-08-10 01:15:59 +03:00
rodzic 370b29ab72
commit 9a5db6c3b5
9 zmienionych plików z 610 dodań i 610 usunięć

Wyświetl plik

@ -1,4 +1,4 @@
bin_PROGRAMS = slowrx
slowrx_CPPFLAGS = -g $(GTKMM_CFLAGS) @SNDFILE_CFLAGS@
slowrx_LDADD = $(GTKMM_LIBS) @SNDFILE_LIBS@ -lfftw3
slowrx_SOURCES = slowrx.cc common.cc modespec.cc gui.cc pcm.cc vis.cc video.cc
slowrx_SOURCES = slowrx.cc common.cc modespec.cc gui.cc dsp.cc vis.cc video.cc

Wyświetl plik

@ -16,7 +16,7 @@ Glib::RefPtr<Gdk::Pixbuf> pixbuf_disp;
Glib::KeyFile config;
vector<thread> threads(2);
std::vector<std::thread> threads(2);
PicMeta CurrentPic;
PcmData pcm;
@ -41,7 +41,7 @@ double rad2deg (double rad) {
return (180 / M_PI) * rad;
}
void ensure_dir_exists(string dir) {
void ensure_dir_exists(std::string dir) {
struct stat buf;
int i = stat(dir.c_str(), &buf);
@ -55,23 +55,23 @@ void ensure_dir_exists(string dir) {
// Save current picture as PNG
void saveCurrentPic() {
Glib::RefPtr<Gdk::Pixbuf> scaledpb;
/*Glib::RefPtr<Gdk::Pixbuf> scaledpb;
stringstream ss;
string basename = config.get_string("slowrx","rxdir");
std::stringstream ss;
std::string basename = config.get_std::string("slowrx","rxdir");
ss << basename << "/" << CurrentPic.timestr << "_" << ModeSpec[CurrentPic.Mode].ShortName;
string pngfilename = ss.str();
std::string pngfilename = ss.str();
cout << " Saving to " << pngfilename << "\n";
scaledpb = pixbuf_rx->scale_simple(ModeSpec[CurrentPic.Mode].ImgWidth,
scaledpb = pixbuf_rx->scale_simple(ModeSpec[CurrentPic.Mode].ScanPixels,
ModeSpec[CurrentPic.Mode].NumLines * ModeSpec[CurrentPic.Mode].LineHeight, Gdk::INTERP_HYPER);
ensure_dir_exists(config.get_string("slowrx","rxdir"));
ensure_dir_exists(config.get_std::string("slowrx","rxdir"));
scaledpb->save(pngfilename, "png");
//g_object_unref(scaledpb);
//g_string_free(pngfilename, true);
//g_std::string_free(pngfilename, true);*/
}

Wyświetl plik

@ -16,8 +16,6 @@
#include "fftw3.h"
#include "gtkmm.h"
using namespace std;
enum WindowType {
WINDOW_HANN47 = 0,
WINDOW_HANN63,
@ -31,12 +29,11 @@ enum WindowType {
enum SSTVMode {
MODE_UNKNOWN=0,
MODE_M1, MODE_M2, MODE_M3, MODE_M4,
MODE_S1, MODE_S2, MODE_SDX,
MODE_R72, MODE_R36, MODE_R24, MODE_R24BW, MODE_R12BW, MODE_R8BW,
MODE_PD50, MODE_PD90, MODE_PD120, MODE_PD160, MODE_PD180, MODE_PD240, MODE_PD290,
MODE_P3, MODE_P5, MODE_P7,
MODE_W2120, MODE_W2180
MODE_M1, MODE_M2, MODE_M3, MODE_M4, MODE_S1,
MODE_S2, MODE_SDX, MODE_R72, MODE_R36, MODE_R24,
MODE_R24BW, MODE_R12BW, MODE_R8BW, MODE_PD50, MODE_PD90,
MODE_PD120, MODE_PD160, MODE_PD180, MODE_PD240, MODE_PD290,
MODE_P3, MODE_P5, MODE_P7, MODE_W2120, MODE_W2180
};
enum eColorEnc {
@ -51,7 +48,7 @@ enum eSubSamp {
SUBSAMP_444, SUBSAMP_422_YUV, SUBSAMP_420_YUYV, SUBSAMP_440_YUVY
};
extern map<int, SSTVMode> vis2mode;
extern std::map<int, SSTVMode> vis2mode;
typedef struct _FFTStuff FFTStuff;
struct _FFTStuff {
@ -79,18 +76,19 @@ class DSPworker {
DSPworker();
void open_audio_file(string);
void read_more();
void open_audio_file(std::string);
double forward(unsigned);
double forward();
double forward_ms(double);
void get_windowed_moment(WindowType, double *);
double get_peak_freq (double, double, WindowType);
int GetBin (double);
double FourierPower (fftw_complex coeff);
void getWindowedMoment(WindowType, double *);
double getPeakFreq (double, double, WindowType);
int getBin (double);
double getFourierPower (fftw_complex coeff);
bool is_still_listening ();
vector<short> getsamples(int);
std::vector<double> getBandPowerPerHz(std::vector<std::vector<double> >);
WindowType getBestWindowFor(SSTVMode, double);
WindowType getBestWindowFor(SSTVMode);
void readMore();
static short win_lens_[8];
static double window_[8][1024];
@ -178,19 +176,19 @@ extern PicMeta CurrentPic;
typedef struct ModeSpec {
string Name;
string ShortName;
double tSync;
double tPorch;
double tSep;
double tScan;
double tLine;
int ImgWidth;
int NumLines;
int LineHeight;
eColorEnc ColorEnc;
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 SubSamp;
eSubSamp SubSampling;
} _ModeSpec;
extern _ModeSpec ModeSpec[];
@ -200,8 +198,8 @@ int clip (double a);
void createGUI ();
double deg2rad (double Deg);
double FindSync (SSTVMode Mode, double Rate, int *Skip);
string GetFSK ();
bool GetVideo (SSTVMode Mode, double Rate, DSPworker *dsp, bool Redraw);
std::string GetFSK ();
bool GetVideo (SSTVMode Mode, DSPworker *dsp);
SSTVMode GetVIS (DSPworker*);
int initPcmDevice (std::string);
void *Listen ();

Wyświetl plik

@ -8,7 +8,7 @@ DSPworker::DSPworker() : Mutex(), please_stop_(false) {
for (int i = 0; i < win_lens_[j]; i++)
window_[j][i] = 0.5 * (1 - cos( (2 * M_PI * i) / (win_lens_[j] - 1)) );
vector<double> cheb = {
std::vector<double> cheb = {
0.0004272315,0.0013212953,0.0032312239,0.0067664313,0.0127521667,0.0222058684,
0.0363037629,0.0563165400,0.0835138389,0.1190416120,0.1637810511,0.2182020094,
0.2822270091,0.3551233730,0.4354402894,0.5210045495,0.6089834347,0.6960162864,
@ -44,12 +44,12 @@ DSPworker::DSPworker() : Mutex(), please_stop_(false) {
printf("DSPworker created\n");
//open_audio_file("/Users/windy/Audio/sig/sstv/robot72-02.wav");
open_audio_file("/Users/windy/Movies/sstv-iss.wav");
open_audio_file("/Users/windy/Audio/sig/sstv/scottie2-01-noiseonly.wav");
//open_audio_file("/Users/windy/Movies/sstv-iss.wav");
//open_audio_file("/Users/windy/Audio/sig/1000Hz-800Hz.wav");
}
void DSPworker::open_audio_file (string fname) {
void DSPworker::open_audio_file (std::string fname) {
file_ = SndfileHandle(fname.c_str()) ;
@ -59,16 +59,14 @@ void DSPworker::open_audio_file (string fname) {
samplerate_ = file_.samplerate();
//puts ("") ;
/* RAII takes care of destroying SndfileHandle object. */
}
int DSPworker::GetBin (double freq) {
int DSPworker::getBin (double freq) {
return (freq / samplerate_ * fft_len_);
}
double DSPworker::FourierPower (fftw_complex coeff) {
double DSPworker::getFourierPower (fftw_complex coeff) {
return pow(coeff[0],2) + pow(coeff[1],2);
}
@ -76,8 +74,7 @@ bool DSPworker::is_still_listening () {
return is_still_listening_;
}
void DSPworker::read_more () {
void DSPworker::readMore () {
short read_buffer[READ_CHUNK_LEN];
sf_count_t samplesread = file_.read(read_buffer, READ_CHUNK_LEN);
@ -108,7 +105,7 @@ double DSPworker::forward (unsigned nsamples) {
cirbuf_tail_ = (cirbuf_tail_ + 1) % CIRBUF_LEN;
cirbuf_fill_count_ -= 1;
if (cirbuf_fill_count_ < MOMENT_LEN) {
read_more();
readMore();
}
}
return (1.0 * nsamples / samplerate_);
@ -121,7 +118,7 @@ double DSPworker::forward_ms(double ms) {
}
// the current moment, windowed
void DSPworker::get_windowed_moment (WindowType win_type, double *result) {
void DSPworker::getWindowedMoment (WindowType win_type, double *result) {
for (int i = 0; i < MOMENT_LEN; i++) {
int win_i = i - MOMENT_LEN/2 + win_lens_[win_type]/2 ;
@ -133,22 +130,22 @@ void DSPworker::get_windowed_moment (WindowType win_type, double *result) {
}
double DSPworker::get_peak_freq (double minf, double maxf, WindowType wintype) {
double DSPworker::getPeakFreq (double minf, double maxf, WindowType wintype) {
double windowed[win_lens_[wintype]];
double Power[fft_len_];
get_windowed_moment(wintype, windowed);
getWindowedMoment(wintype, windowed);
//for (int i=0;i<win_lens_[wintype];i++)
// printf("g %f\n",windowed[i]);
memset (fft_inbuf_, 0, fft_len_ * sizeof(double));
memset(fft_inbuf_, 0, fft_len_ * sizeof(double));
memcpy(fft_inbuf_, windowed, win_lens_[wintype] * sizeof(double));
fftw_execute(fft_plan_);
// Find the bin with most power
int MaxBin = 0;
for (int i = GetBin(minf)-1; i <= GetBin(maxf)+1; i++) {
Power[i] = FourierPower(fft_outbuf_[i]);
if ( (i >= GetBin(minf) && i < GetBin(maxf)) &&
for (int i = getBin(minf)-1; i <= getBin(maxf)+1; i++) {
Power[i] = getFourierPower(fft_outbuf_[i]);
if ( (i >= getBin(minf) && i < getBin(maxf)) &&
(MaxBin == 0 || Power[i] > Power[MaxBin]))
MaxBin = i;
}
@ -164,6 +161,48 @@ double DSPworker::get_peak_freq (double minf, double maxf, WindowType wintype) {
}
std::vector<double> DSPworker::getBandPowerPerHz(std::vector<std::vector<double> > bands) {
double windowed[win_lens_[WINDOW_HANN1023]];
getWindowedMoment(WINDOW_HANN1023, windowed);
memset(fft_inbuf_, 0, fft_len_ * sizeof(double));
memcpy(fft_inbuf_, windowed, win_lens_[WINDOW_HANN1023] * sizeof(double));
fftw_execute(fft_plan_);
std::vector<double> result;
for (std::vector<double> band : bands) {
double P = 0;
int nbins = 0;
for (int i = getBin(band[0]); i <= getBin(band[1]); i++) {
P += getFourierPower(fft_outbuf_[i]);
nbins++;
}
P = P/nbins;
result.push_back(P);
}
return result;
}
WindowType DSPworker::getBestWindowFor(SSTVMode Mode, double SNR) {
WindowType WinType;
if (SNR >= 20) WinType = WINDOW_CHEB47;
else if (SNR >= 10) WinType = WINDOW_HANN63;
else if (SNR >= 9) WinType = WINDOW_HANN95;
else if (SNR >= 3) WinType = WINDOW_HANN127;
else if (SNR >= -5) WinType = WINDOW_HANN255;
else if (SNR >= -10) WinType = WINDOW_HANN511;
else WinType = WINDOW_HANN1023;
// Minimum winlength can be doubled for Scottie DX
//if (Mode == MODE_SDX && WinType < WINDOW_HANN511) WinType++;
return WinType;
}
WindowType DSPworker::getBestWindowFor(SSTVMode Mode) {
return getBestWindowFor(Mode, 99);
}
/*
// Capture fresh PCM data to buffer
void readPcm(int numsamples) {
@ -247,7 +286,7 @@ void populateDeviceList() {
// 0 = opened ok
// -1 = opened, but suboptimal
// -2 = couldn't be opened
int initPcmDevice(string wanted_dev_name) {
int initPcmDevice(std::string wanted_dev_name) {
//snd_pcm_hw_params_t *hwparams;
void *hwparams;

Wyświetl plik

@ -1,4 +1,4 @@
#include "common.h"
#include "common.hh"
/*
*
@ -10,23 +10,15 @@
*
*/
vector<double> WindowHann (int len) {
vector<double> result(len);
for (int i = 0; i < len; i++)
result[i] = 0.5 * (1 - cos( 2 * M_PI * i / (1.0*len-1) ) );
return result;
}
string GetFSK (DSPworker *dsp) {
string GetFSK () {
guint fft_len = 2048, i=0, lo_bin, hi_bin, mid_bin, test_num=0, test_ptr=0;
char *dest;
guint i=0, test_num=0, test_ptr=0;
guchar bit = 0, ascii_byte = 0, byte_ptr = 0, test_bits[24] = {0}, bit_ptr=0;
double hi_pow,lo_pow
vector<double> Hann;
bool is_in_sync = false;
// bit-reversion lookup table
static const guchar bit_rev[] = {
static const guint8 bit_rev[] = {
0x00, 0x20, 0x10, 0x30, 0x08, 0x28, 0x18, 0x38,
0x04, 0x24, 0x14, 0x34, 0x0c, 0x2c, 0x1c, 0x3c,
0x02, 0x22, 0x12, 0x32, 0x0a, 0x2a, 0x1a, 0x3a,
@ -36,43 +28,11 @@ string GetFSK () {
0x03, 0x23, 0x13, 0x33, 0x0b, 0x2b, 0x1b, 0x3b,
0x07, 0x27, 0x17, 0x37, 0x0f, 0x2f, 0x1f, 0x3f };
for (i = 0; i < fft_len; i++) fft.in[i] = 0;
// Create 22ms Hann window
Hann = WindowHann(970);
//for (i = 0; i < 970; i++) Hann[i] = 0.5 * (1 - cos( 2 * M_PI * i / 969.0 ) );
while ( true ) {
// Read data from DSP
readPcm(is_in_sync ? 970: 485);
if (pcm.WindowPtr < 485) {
pcm.WindowPtr += (is_in_sync ? 970 : 485);
continue;
}
// Apply Hann window
for (i = 0; i < 970; i++) fft.in[i] = pcm.Buffer[pcm.WindowPtr+i- 485] * Hann[i];
vector<double> bands = dsp->getBandPowerPerHz({{1850,1950},{2050,2150}});
pcm.WindowPtr += (is_in_sync ? 970 : 485);
// FFT of last 22 ms
fftw_execute(fft.Plan2048);
lo_bin = GetBin(1900+CurrentPic.HedrShift, fft_len)-1;
mid_bin = GetBin(2000+CurrentPic.HedrShift, fft_len);
hi_bin = GetBin(2100+CurrentPic.HedrShift, fft_len)+1;
lo_pow = 0;
hi_pow = 0;
for (i = lo_bin; i <= hi_bin; i++) {
if (i < mid_bin) lo_pow += power(fft.out[i]);
else hi_pow += power(fft.out[i]);
}
bit = (lo_pow > hi_pow) ? 1 : 0;
bit = (bands[0] > bands[1]) ? 1 : 0;
if (!is_in_sync) {

Wyświetl plik

@ -4,19 +4,19 @@
* SSTV mode specifications
* ========================
*
* Name Full human-readable name
* ShortName Abbreviation used in filenames
* ImgWidth Pixels per scanline
* NumLines Number of scanlines
* LineHeight Height of one scanline in pixels (1 or 2)
* Name Full human-readable mode identifier
* ShortName Abbreviation to be used in filenames
* NumLines Total number of scanlines
* HeaderLines Number of lines reserved for header, excluded from 4:3 ratio
* ScanPixels Pixel samples per scanline and channel
* tSync Duration of horizontal sync pulse
* tPorch Duration of sync porch pulse
* tSep Duration of channel separator pulse (+ separator porch)
* tScan Duration of visible part of a channel scan (or Y if YUV)
* tScan Duration of visible part of a channel scan (or Y scan if YUV)
* tLine Time from the beginning of a sync pulse to the beginning
* of the next one
* SyncOrder Positioning of sync pulse (SYNC_SIMPLE, SYNC_SCOTTIE)
* SubSamp Chroma subsampling mode for YUV (SUBSAMP_444, SUBSAMP_2121,
* SubSampling Chroma subsampling mode for YUV (SUBSAMP_444, SUBSAMP_2121,
* SUBSAMP_2112, SUBSAMP_211)
* ColorEnc Color format (COLOR_GBR, COLOR_RGB, COLOR_YUV, COLOR_MONO)
*
@ -30,396 +30,398 @@
*
* Dave Jones KB4YZ (1999): "SSTV modes - line timing".
* <http://www.tima.com/~djones/line.txt>
*
* Dave Jones KB4YZ (1998): "List of SSTV Modes with VIS Codes".
* <http://www.tima.com/~djones/vis.txt>
*/
_ModeSpec ModeSpec[] = {
_ModeSpec ModeSpec[] = {
[MODE_M1] = { // N7CXI, 2000
.Name = "Martin M1",
.ShortName = "M1",
.ImgWidth = 320,
.NumLines = 256,
.LineHeight = 1,
.tSync = 4.862e-3,
.tPorch = 0.572e-3,
.tSep = 0.572e-3,
.tScan = 146.432e-3,
.tLine = 446.446e-3,
.SyncOrder = SYNC_SIMPLE,
.SubSamp = SUBSAMP_444,
.ColorEnc = COLOR_GBR },
[MODE_M1] = { // N7CXI, 2000
.Name = "Martin M1",
.ShortName = "M1",
.ScanPixels = 320,
.NumLines = 256,
.HeaderLines = 16,
.tSync = 4.862e-3,
.tPorch = 0.572e-3,
.tSep = 0.572e-3,
.tScan = 146.432e-3,
.tLine = 446.446e-3,
.SyncOrder = SYNC_SIMPLE,
.SubSampling = SUBSAMP_444,
.ColorEnc = COLOR_GBR },
[MODE_M2] = { // N7CXI, 2000
.Name = "Martin M2",
.ShortName = "M2",
.ImgWidth = 320,
.NumLines = 256,
.LineHeight = 1,
.tScan = 73.216e-3,
.tLine = 226.7986e-3,
.tSync = 4.862e-3,
.tPorch = 0.572e-3,
.tSep = 0.572e-3,
.SyncOrder = SYNC_SIMPLE,
.SubSamp = SUBSAMP_444,
.ColorEnc = COLOR_GBR },
[MODE_M2] = { // N7CXI, 2000
.Name = "Martin M2",
.ShortName = "M2",
.ScanPixels = 320,
.NumLines = 256,
.HeaderLines = 16,
.tScan = 73.216e-3,
.tLine = 226.7986e-3,
.tSync = 4.862e-3,
.tPorch = 0.572e-3,
.tSep = 0.572e-3,
.SyncOrder = SYNC_SIMPLE,
.SubSampling = SUBSAMP_444,
.ColorEnc = COLOR_GBR },
[MODE_M3] = { // KB4YZ, 1999
.Name = "Martin M3",
.ShortName = "M3",
.ImgWidth = 320,
.NumLines = 128,
.LineHeight = 2,
.tScan = 73.216e-3,
.tLine = 446.446e-3,
.tSync = 4.862e-3,
.tPorch = 0.572e-3,
.tSep = 0.572e-3,
.SyncOrder = SYNC_SIMPLE,
.SubSamp = SUBSAMP_444,
.ColorEnc = COLOR_GBR },
[MODE_M3] = { // KB4YZ, 1999
.Name = "Martin M3",
.ShortName = "M3",
.ScanPixels = 320,
.NumLines = 128,
.HeaderLines = 8,
.tScan = 73.216e-3,
.tLine = 446.446e-3,
.tSync = 4.862e-3,
.tPorch = 0.572e-3,
.tSep = 0.572e-3,
.SyncOrder = SYNC_SIMPLE,
.SubSampling = SUBSAMP_444,
.ColorEnc = COLOR_GBR },
[MODE_M4] = { // KB4YZ, 1999
.Name = "Martin M4",
.ShortName = "M4",
.ImgWidth = 320,
.NumLines = 128,
.LineHeight = 2,
.tScan = 73.216e-3,
.tLine = 226.7986e-3,
.tSync = 4.862e-3,
.tPorch = 0.572e-3,
.tSep = 0.572e-3,
.SyncOrder = SYNC_SIMPLE,
.SubSamp = SUBSAMP_444,
.ColorEnc = COLOR_GBR },
[MODE_M4] = { // KB4YZ, 1999
.Name = "Martin M4",
.ShortName = "M4",
.ScanPixels = 320,
.NumLines = 128,
.HeaderLines = 8,
.tScan = 73.216e-3,
.tLine = 226.7986e-3,
.tSync = 4.862e-3,
.tPorch = 0.572e-3,
.tSep = 0.572e-3,
.SyncOrder = SYNC_SIMPLE,
.SubSampling = SUBSAMP_444,
.ColorEnc = COLOR_GBR },
[MODE_S1] = { // N7CXI, 2000
.Name = "Scottie S1",
.ShortName = "S1",
.ImgWidth = 320,
.NumLines = 256,
.LineHeight = 1,
.tSync = 9e-3,
.tPorch = 1.5e-3,
.tSep = 1.5e-3,
.tScan = 138.24e-3,
.tLine = 428.22e-3,
.SyncOrder = SYNC_SCOTTIE,
.SubSamp = SUBSAMP_444,
.ColorEnc = COLOR_GBR },
[MODE_S1] = { // N7CXI, 2000
.Name = "Scottie S1",
.ShortName = "S1",
.ScanPixels = 320,
.NumLines = 256,
.HeaderLines = 16,
.tSync = 9e-3,
.tPorch = 1.5e-3,
.tSep = 1.5e-3,
.tScan = 138.24e-3,
.tLine = 428.22e-3,
.SyncOrder = SYNC_SCOTTIE,
.SubSampling = SUBSAMP_444,
.ColorEnc = COLOR_GBR },
[MODE_S2] = { // N7CXI, 2000
.Name = "Scottie S2",
.ShortName = "S2",
.ImgWidth = 320,
.NumLines = 256,
.LineHeight = 1,
.tScan = 88.064e-3,
.tLine = 277.692e-3,
.tSync = 9e-3,
.tPorch = 1.5e-3,
.tSep = 1.5e-3,
.SyncOrder = SYNC_SCOTTIE,
.SubSamp = SUBSAMP_444,
.ColorEnc = COLOR_GBR },
[MODE_S2] = { // N7CXI, 2000
.Name = "Scottie S2",
.ShortName = "S2",
.ScanPixels = 320,
.NumLines = 256,
.HeaderLines = 16,
.tScan = 88.064e-3,
.tLine = 277.692e-3,
.tSync = 9e-3,
.tPorch = 1.5e-3,
.tSep = 1.5e-3,
.SyncOrder = SYNC_SCOTTIE,
.SubSampling = SUBSAMP_444,
.ColorEnc = COLOR_GBR },
[MODE_SDX] = { // N7CXI, 2000
.Name = "Scottie DX",
.ShortName = "SDX",
.ImgWidth = 320,
.NumLines = 256,
.LineHeight = 1,
.tScan = 345.6e-3,
.tLine = 1050.3e-3,
.tSync = 9e-3,
.tPorch = 1.5e-3,
.tSep = 1.5e-3,
.SyncOrder = SYNC_SCOTTIE,
.SubSamp = SUBSAMP_444,
.ColorEnc = COLOR_GBR },
[MODE_SDX] = { // N7CXI, 2000
.Name = "Scottie DX",
.ShortName = "SDX",
.ScanPixels = 320,
.NumLines = 256,
.HeaderLines = 16,
.tScan = 345.6e-3,
.tLine = 1050.3e-3,
.tSync = 9e-3,
.tPorch = 1.5e-3,
.tSep = 1.5e-3,
.SyncOrder = SYNC_SCOTTIE,
.SubSampling = SUBSAMP_444,
.ColorEnc = COLOR_GBR },
[MODE_R72] = { // N7CXI, 2000
.Name = "Robot 72",
.ShortName = "R72",
.ImgWidth = 320,
.NumLines = 240,
.LineHeight = 1,
.tScan = 138e-3,
.tLine = 300e-3,
.tSync = 9e-3,
.tPorch = 3e-3,
.tSep = 6e-3,
.SyncOrder = SYNC_SIMPLE,
.SubSamp = SUBSAMP_422_YUV,
.ColorEnc = COLOR_YUV },
[MODE_R72] = { // N7CXI, 2000
.Name = "Robot 72",
.ShortName = "R72",
.ScanPixels = 320,
.NumLines = 240,
.HeaderLines = 0,
.tScan = 138e-3,
.tLine = 300e-3,
.tSync = 9e-3,
.tPorch = 3e-3,
.tSep = 6e-3,
.SyncOrder = SYNC_SIMPLE,
.SubSampling = SUBSAMP_422_YUV,
.ColorEnc = COLOR_YUV },
[MODE_R36] = { // N7CXI, 2000
.Name = "Robot 36",
.ShortName = "R36",
.ImgWidth = 320,
.NumLines = 240,
.LineHeight = 1,
.tScan = 88e-3,
.tLine = 150e-3,
.tSync = 9e-3,
.tPorch = 3e-3,
.tSep = 6e-3,
.SyncOrder = SYNC_SIMPLE,
.SubSamp = SUBSAMP_420_YUYV,
.ColorEnc = COLOR_YUV },
[MODE_R36] = { // N7CXI, 2000
.Name = "Robot 36",
.ShortName = "R36",
.ScanPixels = 320,
.NumLines = 240,
.HeaderLines = 0,
.tScan = 88e-3,
.tLine = 150e-3,
.tSync = 9e-3,
.tPorch = 3e-3,
.tSep = 6e-3,
.SyncOrder = SYNC_SIMPLE,
.SubSampling = SUBSAMP_420_YUYV,
.ColorEnc = COLOR_YUV },
[MODE_R24] = { // KB4YZ, 1999
.Name = "Robot 24",
.ShortName = "R24",
.ImgWidth = 320,
.NumLines = 240,
.LineHeight = 1,
.tScan = 66e-3,
.tLine = 150e-3,
.tSync = 9e-3,
.tPorch = 3e-3,
.tSep = 6e-3,
.SyncOrder = SYNC_SIMPLE,
.SubSamp = SUBSAMP_420_YUYV,
.ColorEnc = COLOR_YUV },
[MODE_R24] = { // KB4YZ, 1999
.Name = "Robot 24",
.ShortName = "R24",
.ScanPixels = 320,
.NumLines = 240,
.HeaderLines = 0,
.tScan = 66e-3,
.tLine = 150e-3,
.tSync = 9e-3,
.tPorch = 3e-3,
.tSep = 6e-3,
.SyncOrder = SYNC_SIMPLE,
.SubSampling = SUBSAMP_420_YUYV,
.ColorEnc = COLOR_YUV },
[MODE_R24BW] = { // KB4YZ, 1999
.Name = "Robot 24 B/W",
.ShortName = "R24BW",
.ImgWidth = 320,
.NumLines = 240,
.LineHeight = 1,
.tScan = 66e-3,
.tLine = 100e-3,
.tSync = 7e-3,
.tPorch = 0,
.tSep = 0,
.SyncOrder = SYNC_SIMPLE,
.SubSamp = SUBSAMP_444,
.ColorEnc = COLOR_MONO },
[MODE_R24BW] = { // KB4YZ, 1999
.Name = "Robot 24 B/W",
.ShortName = "R24BW",
.ScanPixels = 320,
.NumLines = 240,
.HeaderLines = 0,
.tScan = 66e-3,
.tLine = 100e-3,
.tSync = 7e-3,
.tPorch = 0,
.tSep = 0,
.SyncOrder = SYNC_SIMPLE,
.SubSampling = SUBSAMP_444,
.ColorEnc = COLOR_MONO },
[MODE_R12BW] = { // KB4YZ, 1999
.Name = "Robot 12 B/W",
.ShortName = "R12BW",
.ImgWidth = 320,
.NumLines = 120,
.LineHeight = 2,
.tScan = 66e-3,
.tLine = 100e-3,
.tSync = 7e-3,
.tPorch = 0,
.tSep = 0,
.SyncOrder = SYNC_SIMPLE,
.SubSamp = SUBSAMP_444,
.ColorEnc = COLOR_MONO },
[MODE_R12BW] = { // KB4YZ, 1999
.Name = "Robot 12 B/W",
.ShortName = "R12BW",
.ScanPixels = 320,
.NumLines = 120,
.HeaderLines = 0,
.tScan = 66e-3,
.tLine = 100e-3,
.tSync = 7e-3,
.tPorch = 0,
.tSep = 0,
.SyncOrder = SYNC_SIMPLE,
.SubSampling = SUBSAMP_444,
.ColorEnc = COLOR_MONO },
[MODE_R8BW] = { // KB4YZ, 1999
.Name = "Robot 8 B/W",
.ShortName = "R8BW",
.ImgWidth = 320,
.NumLines = 120,
.LineHeight = 2,
.tScan = 60e-3,
.tLine = 67e-3,
.tSync = 7e-3,
.tPorch = 0,
.tSep = 0,
.SyncOrder = SYNC_SIMPLE,
.SubSamp = SUBSAMP_444,
.ColorEnc = COLOR_MONO },
[MODE_R8BW] = { // KB4YZ, 1999
.Name = "Robot 8 B/W",
.ShortName = "R8BW",
.ScanPixels = 320,
.NumLines = 120,
.HeaderLines = 0,
.tScan = 60e-3,
.tLine = 67e-3,
.tSync = 7e-3,
.tPorch = 0,
.tSep = 0,
.SyncOrder = SYNC_SIMPLE,
.SubSampling = SUBSAMP_444,
.ColorEnc = COLOR_MONO },
[MODE_W2120] = { // KB4YZ, 1999
.Name = "Wraase SC-2 120",
.ShortName = "W2120",
.ImgWidth = 320,
.NumLines = 256,
.LineHeight = 1,
.tScan = 156.5025e-3,
.tLine = 475.530018e-3,
.tSync = 5.5225e-3,
.tPorch = 0.5e-3,
.tSep = 0,
.SyncOrder = SYNC_SIMPLE,
.SubSamp = SUBSAMP_444,
.ColorEnc = COLOR_RGB },
[MODE_W2120] = { // KB4YZ, 1999
.Name = "Wraase SC-2 120",
.ShortName = "W2120",
.ScanPixels = 320,
.NumLines = 256,
.HeaderLines = 16,
.tScan = 156.5025e-3,
.tLine = 475.530018e-3,
.tSync = 5.5225e-3,
.tPorch = 0.5e-3,
.tSep = 0,
.SyncOrder = SYNC_SIMPLE,
.SubSampling = SUBSAMP_444,
.ColorEnc = COLOR_RGB },
[MODE_W2180] = { // N7CXI, 2000
.Name = "Wraase SC-2 180",
.ShortName = "W2180",
.ImgWidth = 320,
.NumLines = 256,
.LineHeight = 1,
.tScan = 235e-3,
.tLine = 711.0225e-3,
.tSync = 5.5225e-3,
.tPorch = 0.5e-3,
.tSep = 0,
.SyncOrder = SYNC_SIMPLE,
.SubSamp = SUBSAMP_444,
.ColorEnc = COLOR_RGB },
[MODE_W2180] = { // N7CXI, 2000
.Name = "Wraase SC-2 180",
.ShortName = "W2180",
.ScanPixels = 320,
.NumLines = 256,
.HeaderLines = 16,
.tScan = 235e-3,
.tLine = 711.0225e-3,
.tSync = 5.5225e-3,
.tPorch = 0.5e-3,
.tSep = 0,
.SyncOrder = SYNC_SIMPLE,
.SubSampling = SUBSAMP_444,
.ColorEnc = COLOR_RGB },
[MODE_PD50] = { // N7CXI, 2000
.Name = "PD-50",
.ShortName = "PD50",
.ImgWidth = 320,
.NumLines = 256,
.LineHeight = 1,
.tScan = 91.52e-3,
.tLine = 388.16e-3,
.tSync = 20e-3,
.tPorch = 2.08e-3,
.tSep = 0,
.SyncOrder = SYNC_SIMPLE,
.SubSamp = SUBSAMP_440_YUVY,
.ColorEnc = COLOR_YUV },
[MODE_PD50] = { // N7CXI, 2000
.Name = "PD-50",
.ShortName = "PD50",
.ScanPixels = 320,
.NumLines = 256,
.HeaderLines = 16,
.tScan = 91.52e-3,
.tLine = 388.16e-3,
.tSync = 20e-3,
.tPorch = 2.08e-3,
.tSep = 0,
.SyncOrder = SYNC_SIMPLE,
.SubSampling = SUBSAMP_440_YUVY,
.ColorEnc = COLOR_YUV },
[MODE_PD90] = { // N7CXI, 2000
.Name = "PD-90",
.ShortName = "PD90",
.ImgWidth = 320,
.NumLines = 256,
.LineHeight = 1,
.tScan = 170.240e-3,
.tLine = 703.04e-3,
.tSync = 20e-3,
.tPorch = 2.08e-3,
.tSep = 0,
.SyncOrder = SYNC_SIMPLE,
.SubSamp = SUBSAMP_440_YUVY,
.ColorEnc = COLOR_YUV },
[MODE_PD90] = { // N7CXI, 2000
.Name = "PD-90",
.ShortName = "PD90",
.ScanPixels = 320,
.NumLines = 256,
.HeaderLines = 16,
.tScan = 170.240e-3,
.tLine = 703.04e-3,
.tSync = 20e-3,
.tPorch = 2.08e-3,
.tSep = 0,
.SyncOrder = SYNC_SIMPLE,
.SubSampling = SUBSAMP_440_YUVY,
.ColorEnc = COLOR_YUV },
[MODE_PD120] = { // N7CXI, 2000
.Name = "PD-120",
.ShortName = "PD120",
.ImgWidth = 640,
.NumLines = 496,
.LineHeight = 1,
.tScan = 121.6e-3,
.tLine = 508.48e-3,
.tSync = 20e-3,
.tPorch = 2.08e-3,
.tSep = 0,
.SyncOrder = SYNC_SIMPLE,
.SubSamp = SUBSAMP_440_YUVY,
.ColorEnc = COLOR_YUV },
[MODE_PD120] = { // N7CXI, 2000
.Name = "PD-120",
.ShortName = "PD120",
.ScanPixels = 640,
.NumLines = 496,
.HeaderLines = 16,
.tScan = 121.6e-3,
.tLine = 508.48e-3,
.tSync = 20e-3,
.tPorch = 2.08e-3,
.tSep = 0,
.SyncOrder = SYNC_SIMPLE,
.SubSampling = SUBSAMP_440_YUVY,
.ColorEnc = COLOR_YUV },
[MODE_PD160] = { // N7CXI, 2000
.Name = "PD-160",
.ShortName = "PD160",
.ImgWidth = 512,
.NumLines = 400,
.LineHeight = 1,
.tScan = 195.584e-3,
.tLine = 804.416e-3,
.tSync = 20e-3,
.tPorch = 2.08e-3,
.tSep = 0,
.SyncOrder = SYNC_SIMPLE,
.SubSamp = SUBSAMP_440_YUVY,
.ColorEnc = COLOR_YUV },
[MODE_PD160] = { // N7CXI, 2000
.Name = "PD-160",
.ShortName = "PD160",
.ScanPixels = 512,
.NumLines = 400,
.HeaderLines = 16,
.tScan = 195.584e-3,
.tLine = 804.416e-3,
.tSync = 20e-3,
.tPorch = 2.08e-3,
.tSep = 0,
.SyncOrder = SYNC_SIMPLE,
.SubSampling = SUBSAMP_440_YUVY,
.ColorEnc = COLOR_YUV },
[MODE_PD180] = { // N7CXI, 2000
.Name = "PD-180",
.ShortName = "PD180",
.ImgWidth = 640,
.NumLines = 496,
.LineHeight = 1,
.tScan = 183.04e-3,
.tLine = 754.24e-3,
.tSync = 20e-3,
.tPorch = 2.08e-3,
.tSep = 0,
.SyncOrder = SYNC_SIMPLE,
.SubSamp = SUBSAMP_440_YUVY,
.ColorEnc = COLOR_YUV },
[MODE_PD180] = { // N7CXI, 2000
.Name = "PD-180",
.ShortName = "PD180",
.ScanPixels = 640,
.NumLines = 496,
.HeaderLines = 16,
.tScan = 183.04e-3,
.tLine = 754.24e-3,
.tSync = 20e-3,
.tPorch = 2.08e-3,
.tSep = 0,
.SyncOrder = SYNC_SIMPLE,
.SubSampling = SUBSAMP_440_YUVY,
.ColorEnc = COLOR_YUV },
[MODE_PD240] = { // N7CXI, 2000
.Name = "PD-240",
.ShortName = "PD240",
.ImgWidth = 640,
.NumLines = 496,
.LineHeight = 1,
.tScan = 244.48e-3,
.tLine = 1000e-3,
.tSync = 20e-3,
.tPorch = 2.08e-3,
.tSep = 0,
.SyncOrder = SYNC_SIMPLE,
.SubSamp = SUBSAMP_440_YUVY,
.ColorEnc = COLOR_YUV },
[MODE_PD240] = { // N7CXI, 2000
.Name = "PD-240",
.ShortName = "PD240",
.ScanPixels = 640,
.NumLines = 496,
.HeaderLines = 16,
.tScan = 244.48e-3,
.tLine = 1000e-3,
.tSync = 20e-3,
.tPorch = 2.08e-3,
.tSep = 0,
.SyncOrder = SYNC_SIMPLE,
.SubSampling = SUBSAMP_440_YUVY,
.ColorEnc = COLOR_YUV },
[MODE_PD290] = { // N7CXI, 2000
.Name = "PD-290",
.ShortName = "PD290",
.ImgWidth = 800,
.NumLines = 616,
.LineHeight = 1,
.tScan = 228.8e-3,
.tLine = 937.28e-3,
.tSync = 20e-3,
.tPorch = 2.08e-3,
.tSep = 0,
.SyncOrder = SYNC_SIMPLE,
.SubSamp = SUBSAMP_440_YUVY,
.ColorEnc = COLOR_YUV },
[MODE_PD290] = { // N7CXI, 2000
.Name = "PD-290",
.ShortName = "PD290",
.ScanPixels = 800,
.NumLines = 616,
.HeaderLines = 16,
.tScan = 228.8e-3,
.tLine = 937.28e-3,
.tSync = 20e-3,
.tPorch = 2.08e-3,
.tSep = 0,
.SyncOrder = SYNC_SIMPLE,
.SubSampling = SUBSAMP_440_YUVY,
.ColorEnc = COLOR_YUV },
[MODE_P3] = { // N7CXI, 2000
.Name = "Pasokon P3",
.ShortName = "P3",
.ImgWidth = 640,
.NumLines = 496,
.LineHeight = 1,
.tScan = 133.333e-3,
.tLine = 409.375e-3,
.tSync = 5.208e-3,
.tPorch = 1.042e-3,
.tSep = 1.042e-3,
.SyncOrder = SYNC_SIMPLE,
.SubSamp = SUBSAMP_444,
.ColorEnc = COLOR_RGB },
[MODE_P3] = { // N7CXI, 2000
.Name = "Pasokon P3",
.ShortName = "P3",
.ScanPixels = 640,
.NumLines = 496,
.HeaderLines = 16,
.tScan = 133.333e-3,
.tLine = 409.375e-3,
.tSync = 5.208e-3,
.tPorch = 1.042e-3,
.tSep = 1.042e-3,
.SyncOrder = SYNC_SIMPLE,
.SubSampling = SUBSAMP_444,
.ColorEnc = COLOR_RGB },
[MODE_P5] = { // N7CXI, 2000
.Name = "Pasokon P5",
.ShortName = "P5",
.ImgWidth = 640,
.NumLines = 496,
.LineHeight = 1,
.tScan = 200e-3,
.tLine = 614.065e-3,
.tSync = 7.813e-3,
.tPorch = 1.563e-3,
.tSep = 1.563e-3,
.SyncOrder = SYNC_SIMPLE,
.SubSamp = SUBSAMP_444,
.ColorEnc = COLOR_RGB },
[MODE_P5] = { // N7CXI, 2000
.Name = "Pasokon P5",
.ShortName = "P5",
.ScanPixels = 640,
.NumLines = 496,
.HeaderLines = 16,
.tScan = 200e-3,
.tLine = 614.065e-3,
.tSync = 7.813e-3,
.tPorch = 1.563e-3,
.tSep = 1.563e-3,
.SyncOrder = SYNC_SIMPLE,
.SubSampling = SUBSAMP_444,
.ColorEnc = COLOR_RGB },
[MODE_P7] = { // N7CXI, 2000
.Name = "Pasokon P7",
.ShortName = "P7",
.ImgWidth = 640,
.NumLines = 496,
.LineHeight = 1,
.tScan = 266.666e-3,
.tLine = 818.747e-3,
.tSync = 10.417e-3,
.tPorch = 2.083e-3,
.tSep = 2.083e-3,
.SyncOrder = SYNC_SIMPLE,
.SubSamp = SUBSAMP_444,
.ColorEnc = COLOR_RGB }
[MODE_P7] = { // N7CXI, 2000
.Name = "Pasokon P7",
.ShortName = "P7",
.ScanPixels = 640,
.NumLines = 496,
.HeaderLines = 16,
.tScan = 266.666e-3,
.tLine = 818.747e-3,
.tSync = 10.417e-3,
.tPorch = 2.083e-3,
.tSep = 2.083e-3,
.SyncOrder = SYNC_SIMPLE,
.SubSampling = SUBSAMP_444,
.ColorEnc = COLOR_RGB }
};
/*
* Mapping of 7-bit VIS codes to modes
*
* Reference: Dave Jones KB4YZ (1998): "List of SSTV Modes with VIS Codes".
* http://www.tima.com/~djones/vis.txt
* KB4YZ, 1998
*
*/
map<int, SSTVMode> vis2mode = {
std::map<int, SSTVMode> vis2mode = {
{0x02,MODE_R8BW}, {0x04,MODE_R24}, {0x06,MODE_R12BW}, {0x08,MODE_R36},
{0x0A,MODE_R24BW},{0x0C,MODE_R72}, {0x20,MODE_M4}, {0x24,MODE_M3},
{0x28,MODE_M2}, {0x2C,MODE_M1}, {0x37,MODE_W2180}, {0x38,MODE_S2},

Wyświetl plik

@ -2,12 +2,12 @@
int main(int argc, char *argv[]) {
string confpath(string(getenv("HOME")) + "/.config/slowrx/slowrx.ini");
std::string confpath(std::string(getenv("HOME")) + "/.config/slowrx/slowrx.ini");
config.load_from_file(confpath);
DSPworker dsp;
GetVideo(GetVIS(&dsp), 44100, &dsp, false);
GetVideo(GetVIS(&dsp), &dsp);
//SlowGUI gui = SlowGUI();
return 0;

Wyświetl plik

@ -5,16 +5,17 @@ typedef struct {
int Y;
int Channel;
double Time;
} _Pixel;
} PixelSample;
bool sortPixelTime(_Pixel a, _Pixel b) { return a.Time < b.Time; }
bool sortPixelSamples(PixelSample a, PixelSample b) { return a.Time < b.Time; }
// 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].ImgWidth; x++) {
for (int x = 0; x < ModeSpec[Mode].ScanPixels; x++) {
for (int y = 0; y < ModeSpec[Mode].NumLines; y++) {
p = pixels + y * rowstride + x * 3;
@ -49,36 +50,21 @@ void toPixbufRGB(guint8 Image[800][800][3], Glib::RefPtr<Gdk::Pixbuf> pixbuf, SS
}
}
/* Demodulate the video signal & store all kinds of stuff for later stages
* Mode: M1, M2, S1, S2, R72, R36...
* Rate: exact sampling rate used
* Skip: number of PCM samples to skip at the beginning (for sync phase adjustment)
* 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, double Rate, DSPworker* dsp, bool Redraw) {
printf("receive %s\n",ModeSpec[Mode].Name.c_str());
vector<_Pixel> PixelGrid;
// Time instants for all pixels
std::vector<PixelSample> getPixelSamplingPoints(SSTVMode Mode) {
_ModeSpec s = ModeSpec[Mode];
guint8 Image[800][800][3];
// Time instants for all pixels
std::vector<PixelSample> PixelGrid;
for (int y=0; y<s.NumLines; y++) {
for (int x=0; x<s.ImgWidth; x++) {
for (int x=0; x<s.ScanPixels; x++) {
for (int Chan=0; Chan < (s.ColorEnc == COLOR_MONO ? 1 : 3); Chan++) {
_Pixel px;
PixelSample px;
px.X = x;
px.Y = y;
px.Channel = Chan;
switch(s.SubSamp) {
switch(s.SubSampling) {
case (SUBSAMP_444):
px.Time = y*(s.tLine) + (x+0.5)/s.ImgWidth * s.tScan;
px.Time = y*(s.tLine) + (x+0.5)/s.ScanPixels * s.tScan;
switch (s.SyncOrder) {
@ -98,15 +84,18 @@ bool GetVideo(SSTVMode Mode, double Rate, DSPworker* dsp, bool Redraw) {
switch (Chan) {
case (0):
px.Time = y*(s.tLine) + s.tSync + s.tPorch + (x+.5)/s.ImgWidth * s.tScan;
px.Time = y*(s.tLine) + s.tSync + s.tPorch +
(x+.5)/s.ScanPixels * s.tScan;
break;
case (1):
px.Time = (y-(y % 2)) * (s.tLine) + s.tSync + s.tPorch + s.tScan + s.tSep + 0.5*(x+0.5)/s.ImgWidth * s.tScan;
px.Time = (y-(y % 2)) * (s.tLine) + s.tSync + s.tPorch +
s.tScan + s.tSep + 0.5*(x+0.5)/s.ScanPixels * s.tScan;
break;
case (2):
px.Time = (y+1-(y % 2)) * (s.tLine) + s.tSync + s.tPorch + s.tScan + s.tSep + 0.5*(x+0.5)/s.ImgWidth * s.tScan;
px.Time = (y+1-(y % 2)) * (s.tLine) + s.tSync + s.tPorch +
s.tScan + s.tSep + 0.5*(x+0.5)/s.ScanPixels * s.tScan;
break;
}
@ -116,17 +105,19 @@ bool GetVideo(SSTVMode Mode, double Rate, DSPworker* dsp, bool Redraw) {
switch (Chan) {
case (0):
px.Time = y*(s.tLine) + s.tSync + s.tPorch + (x+.5)/s.ImgWidth * s.tScan;
px.Time = y*(s.tLine) + s.tSync + s.tPorch +
(x+.5)/s.ScanPixels * s.tScan;
break;
case (1):
px.Time = y*(s.tLine) + s.tSync + s.tPorch + s.tScan
+ s.tSep + (x+.5)/s.ImgWidth * s.tScan/2;
+ s.tSep + (x+.5)/s.ScanPixels * s.tScan/2;
break;
case (2):
px.Time = y*(s.tLine) + s.tSync + s.tPorch + s.tScan
+ s.tSep + s.tScan/2 + s.tSep + (x+.5)/s.ImgWidth * s.tScan/2;
+ s.tSep + s.tScan/2 + s.tSep + (x+.5)/s.ScanPixels *
s.tScan/2;
break;
}
@ -137,17 +128,18 @@ bool GetVideo(SSTVMode Mode, double Rate, DSPworker* dsp, bool Redraw) {
case (0):
px.Time = (y/2)*(s.tLine) + s.tSync + s.tPorch +
((y%2 == 1 ? s.ImgWidth*3 : 0)+x+.5)/s.ImgWidth * s.tScan;
((y%2 == 1 ? s.ScanPixels*3 : 0)+x+.5)/s.ScanPixels *
s.tScan;
break;
case (1):
px.Time = (y/2)*(s.tLine) + s.tSync + s.tPorch +
(s.ImgWidth+x+.5)/s.ImgWidth * s.tScan;
(s.ScanPixels+x+.5)/s.ScanPixels * s.tScan;
break;
case (2):
px.Time = (y/2)*(s.tLine) + s.tSync + s.tPorch +
(s.ImgWidth*2+x+.5)/s.ImgWidth * s.tScan;
(s.ScanPixels*2+x+.5)/s.ScanPixels * s.tScan;
break;
}
@ -158,12 +150,29 @@ bool GetVideo(SSTVMode Mode, double Rate, DSPworker* dsp, bool Redraw) {
}
}
std::sort(PixelGrid.begin(), PixelGrid.end(), sortPixelTime);
std::sort(PixelGrid.begin(), PixelGrid.end(), sortPixelSamples);
Glib::RefPtr<Gtk::Application> app =
Gtk::Application::create("com.windytan.slowrx");
return PixelGrid;
}
/* Demodulate the video signal & store all kinds of stuff for later stages
* Mode: M1, M2, S1, S2, R72, R36...
* Rate: exact sampling rate used
* Skip: number of PCM samples to skip at the beginning (for sync phase adjustment)
* 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) {
printf("receive %s\n",ModeSpec[Mode].Name.c_str());
_ModeSpec s = ModeSpec[Mode];
guint8 Image[800][800][3];
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.ImgWidth, s.NumLines);
pixbuf_rx = Gdk::Pixbuf::create(Gdk::COLORSPACE_RGB, false, 8, s.ScanPixels, s.NumLines);
pixbuf_rx->fill(0x000000ff);
// Initialize pixbuffer
@ -173,11 +182,7 @@ bool GetVideo(SSTVMode Mode, double Rate, DSPworker* dsp, bool Redraw) {
gdk_pixbuf_fill(pixbuf_rx, 0);
}*/
/*int rowstride = gdk_pixbuf_get_rowstride (pixbuf_rx);
guchar *pixels, *p;
pixels = gdk_pixbuf_get_pixels(pixbuf_rx);
g_object_unref(pixbuf_disp);
/*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);
*/
@ -189,127 +194,127 @@ bool GetVideo(SSTVMode Mode, double Rate, DSPworker* dsp, bool Redraw) {
SyncSampleNum = 0;*/
// Loop through signal
std::vector<PixelSample> PixelGrid = getPixelSamplingPoints(Mode);
double t = 0;
for (int PixelIdx = 0; PixelIdx < PixelGrid.size(); PixelIdx++) {
double Lum = 0;
if (!Redraw) {
while (t < PixelGrid[PixelIdx].Time && dsp->is_still_listening()) {
t += dsp->forward();
}
while (t < PixelGrid[PixelIdx].Time && dsp->is_still_listening()) {
t += dsp->forward();
}
/*** Store the sync band for later adjustments ***/
/*** Store the sync band for later adjustments ***/
/*if (SampleNum == NextSyncTime) {
/*if (SampleNum == NextSyncTime) {
Praw = Psync = 0;
Praw = Psync = 0;
memset(fft.in, 0, sizeof(double)*FFTLen);
// Hann window
for (i = 0; i < 64; i++) fft.in[i] = pcm.Buffer[pcm.WindowPtr+i-32] / 32768.0 * Hann[1][i];
memset(fft.in, 0, sizeof(double)*FFTLen);
// Hann window
for (i = 0; i < 64; i++) fft.in[i] = pcm.Buffer[pcm.WindowPtr+i-32] / 32768.0 * Hann[1][i];
fftw_execute(fft.Plan1024);
fftw_execute(fft.Plan1024);
for (i=GetBin(1500+CurrentPic.HedrShift,FFTLen); i<=GetBin(2300+CurrentPic.HedrShift, FFTLen); i++)
Praw += power(fft.out[i]);
for (i=GetBin(1500+CurrentPic.HedrShift,FFTLen); i<=GetBin(2300+CurrentPic.HedrShift, FFTLen); i++)
Praw += power(fft.out[i]);
for (i=SyncTargetBin-1; i<=SyncTargetBin+1; i++)
Psync += power(fft.out[i]) * (1- .5*abs(SyncTargetBin-i));
for (i=SyncTargetBin-1; i<=SyncTargetBin+1; i++)
Psync += power(fft.out[i]) * (1- .5*abs(SyncTargetBin-i));
Praw /= (GetBin(2300+CurrentPic.HedrShift, FFTLen) - GetBin(1500+CurrentPic.HedrShift, FFTLen));
Psync /= 2.0;
Praw /= (GetBin(2300+CurrentPic.HedrShift, FFTLen) - GetBin(1500+CurrentPic.HedrShift, FFTLen));
Psync /= 2.0;
// 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
HasSync[SyncSampleNum] = (Psync > 2*Praw);
// 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
HasSync[SyncSampleNum] = (Psync > 2*Praw);
NextSyncTime += 13;
SyncSampleNum ++;
NextSyncTime += 13;
SyncSampleNum ++;
}*/
}*/
/*** Estimate SNR ***/
/*** Estimate SNR ***/
/*if (SampleNum == NextSNRtime) {
memset(fft.in, 0, sizeof(double)*FFTLen);
double SNR;
// Apply Hann window
for (i = 0; i < FFTLen; i++) fft.in[i] = pcm.Buffer[pcm.WindowPtr + i - FFTLen/2] / 32768.0 * Hann[6][i];
if (PixelIdx == 0 || PixelGrid[PixelIdx].X == s.ScanPixels/2) {
std::vector<double> bands = dsp->getBandPowerPerHz({{400,800}, {1500,2300}, {2700, 3400}});
double Pvideo_plus_noise = bands[1];
double Pnoise_only = (bands[0] + bands[2]) / 2;
double Psignal = Pvideo_plus_noise - Pnoise_only;
fftw_execute(fft.Plan1024);
SNR = ((Psignal / Pnoise_only < .01) ? -20 : 10 * log10(Psignal / Pnoise_only));
// Calculate video-plus-noise power (1500-2300 Hz)
printf("SNR = %f\n",SNR);
Pvideo_plus_noise = 0;
for (n = GetBin(1500+CurrentPic.HedrShift, FFTLen); n <= GetBin(2300+CurrentPic.HedrShift, FFTLen); n++)
Pvideo_plus_noise += power(fft.out[n]);
}
// Calculate noise-only power (400-800 Hz + 2700-3400 Hz)
Pnoise_only = 0;
for (n = GetBin(400+CurrentPic.HedrShift, FFTLen); n <= GetBin(800+CurrentPic.HedrShift, FFTLen); n++)
Pnoise_only += power(fft.out[n]);
for (n = GetBin(2700+CurrentPic.HedrShift, FFTLen); n <= GetBin(3400+CurrentPic.HedrShift, FFTLen); n++)
Pnoise_only += power(fft.out[n]);
// Bandwidths
VideoPlusNoiseBins = GetBin(2300, FFTLen) - GetBin(1500, FFTLen) + 1;
NoiseOnlyBins = GetBin(800, FFTLen) - GetBin(400, FFTLen) + 1 +
GetBin(3400, FFTLen) - GetBin(2700, FFTLen) + 1;
ReceiverBins = GetBin(3400, FFTLen) - GetBin(400, FFTLen);
// Eq 15
Pnoise = Pnoise_only * (1.0 * ReceiverBins / NoiseOnlyBins);
Psignal = Pvideo_plus_noise - Pnoise_only * (1.0 * VideoPlusNoiseBins / NoiseOnlyBins);
// Lower bound to -20 dB
SNR = ((Psignal / Pnoise < .01) ? -20 : 10 * log10(Psignal / Pnoise));
NextSNRtime += 256;
}*/
/*** FM demodulation ***/
//PrevFreq = Freq;
// Adapt window size to SNR
WindowType WinType;
double SNR = 10;
bool Adaptive = true;
if (!Adaptive) WinType = WINDOW_CHEB47;
/*if (SampleNum == NextSNRtime) {
else if (SNR >= 20) WinType = WINDOW_CHEB47;
else if (SNR >= 10) WinType = WINDOW_HANN63;
else if (SNR >= 9) WinType = WINDOW_HANN95;
else if (SNR >= 3) WinType = WINDOW_HANN127;
else if (SNR >= -5) WinType = WINDOW_HANN255;
else if (SNR >= -10) WinType = WINDOW_HANN511;
else WinType = WINDOW_HANN1023;
memset(fft.in, 0, sizeof(double)*FFTLen);
// Minimum winlength can be doubled for Scottie DX
//if (Mode == MODE_SDX && WinType < WINDOW_HANN511) WinType++;
// Apply Hann window
for (i = 0; i < FFTLen; i++) fft.in[i] = pcm.Buffer[pcm.WindowPtr + i - FFTLen/2] / 32768.0 * Hann[6][i];
double Freq = dsp->get_peak_freq(1500, 2300, WinType);
fftw_execute(fft.Plan1024);
// Linear interpolation of (chronologically) intermediate frequencies, for redrawing
//InterpFreq = PrevFreq + (Freq-PrevFreq) * ... // TODO!
// Calculate video-plus-noise power (1500-2300 Hz)
// Calculate luminency & store for later use
Lum = clip((Freq - (1500)) / 3.1372549);
measured.push_back({t, Lum});
//StoredLum[SampleNum] = clip((Freq - (1500 + CurrentPic.HedrShift)) / 3.1372549);
Pvideo_plus_noise = 0;
for (n = GetBin(1500+CurrentPic.HedrShift, FFTLen); n <= GetBin(2300+CurrentPic.HedrShift, FFTLen); n++)
Pvideo_plus_noise += power(fft.out[n]);
} /* endif (!Redraw) */
// Calculate noise-only power (400-800 Hz + 2700-3400 Hz)
Pnoise_only = 0;
for (n = GetBin(400+CurrentPic.HedrShift, FFTLen); n <= GetBin(800+CurrentPic.HedrShift, FFTLen); n++)
Pnoise_only += power(fft.out[n]);
for (n = GetBin(2700+CurrentPic.HedrShift, FFTLen); n <= GetBin(3400+CurrentPic.HedrShift, FFTLen); n++)
Pnoise_only += power(fft.out[n]);
// Bandwidths
VideoPlusNoiseBins = GetBin(2300, FFTLen) - GetBin(1500, FFTLen) + 1;
NoiseOnlyBins = GetBin(800, FFTLen) - GetBin(400, FFTLen) + 1 +
GetBin(3400, FFTLen) - GetBin(2700, FFTLen) + 1;
ReceiverBins = GetBin(3400, FFTLen) - GetBin(400, FFTLen);
// Eq 15
Pnoise = Pnoise_only * (1.0 * ReceiverBins / NoiseOnlyBins);
Psignal = Pvideo_plus_noise - Pnoise_only * (1.0 * VideoPlusNoiseBins / NoiseOnlyBins);
// Lower bound to -20 dB
SNR = ((Psignal / Pnoise < .01) ? -20 : 10 * log10(Psignal / Pnoise));
NextSNRtime += 256;
}*/
/*** FM demodulation ***/
//PrevFreq = Freq;
// Adapt window size to SNR
bool Adaptive = true;
WindowType WinType;
if (Adaptive) WinType = dsp->getBestWindowFor(Mode, SNR);
else WinType = dsp->getBestWindowFor(Mode);
double Freq = dsp->getPeakFreq(1500, 2300, WinType);
// Linear interpolation of (chronologically) intermediate frequencies, for redrawing
//InterpFreq = PrevFreq + (Freq-PrevFreq) * ... // TODO!
// Calculate luminency & store for later use
Lum = clip((Freq - (1500)) / 3.1372549);
//measured.push_back({t, Lum});
//StoredLum[SampleNum] = clip((Freq - (1500 + CurrentPic.HedrShift)) / 3.1372549);
int x = PixelGrid[PixelIdx].X;
int y = PixelGrid[PixelIdx].Y;

Wyświetl plik

@ -30,7 +30,7 @@ SSTVMode GetVIS (DSPworker *dsp) {
//if (Abort || ManualResync) return(0);
HedrBuf[HedrPtr] = dsp->get_peak_freq(500, 3300, WINDOW_HANN1023);
HedrBuf[HedrPtr] = dsp->getPeakFreq(500, 3300, WINDOW_HANN1023);
dsp->forward_ms(10);
// Header buffer holds 45 * 10 msec = 450 msec
@ -123,13 +123,9 @@ SSTVMode GetVIS (DSPworker *dsp) {
ptr = 0;
}*/
//pcm.WindowPtr += 441;
}
// Skip the rest of the stop bit
//readPcm(20e-3 * 44100);
//pcm.WindowPtr += 20e-3 * 44100;
dsp->forward_ms(12); // TODO hack
if (vis2mode.find(vis) != vis2mode.end()) {