Signal Analyzer (2)

To show the density, we need a two-dimensional array to keep track the shape of the spectrum for each scan.

/* save data in vv[][] */
for(int ix=0;ix<spectrum_x;ix++) {
	int iy = (1.0-val)*density_y*0.9 + density_y*0.1;
	vv[iy][ix]++;
	if(vv[iy][ix] > vvmax) vvmax = vv[iy][ix];
}

/* density draw */
	p2 = m_image2->get_pixels();
	for (int j = 0; j < density_y; j++) {
		for (int i = 0; i < density_x; i++) {
			double vvv = double (vv[j][i]) / double (vvmax);
			*p2++ = colormap_r( vvv );
			*p2++ = colormap_g( vvv );
			*p2++ = colormap_b( vvv );
		}
	}

IC-7410 Rig Control Program (C++ version), build=123_048

Signal Analyzer

IC-7410 Rig Control Program (C++ version), build=123_046

Beneath the waterfall windows, there are new windows in which spectrum is shown in a density or histogram format.

Looks like an analyzer from Keysight Technologies?

Selection_047

Widget arrangement

IC-7410 Rig Control Program (C++ version)_043

If you prefer to keep your code simple, it might be difficult to arrange the widgets optimally.

int main(int argc, char *argv[]) {
	vector <Sound*> slist;
	SoundIC7410 s1{argv[1]};
	SoundSoft66 s2{argv[2]};
	slist.push_back(&s1);
	slist.push_back(&s2);

	vector <Rig*> rlist;
	RigIC7410 r1{argv[3]};
	RigSoft66 r2{nullptr};
	rlist.push_back(&r1);
	rlist.push_back(&r2);

	int argc_dummy = 1; /* to ignore argv[>=1] */
	auto app = Gtk::Application::create(argc_dummy, argv, "app.id");
	MyWindow win(slist, rlist);
	return app->run(win);
}
MyWindow::MyWindow(const vector <Sound*> &slist, const vector <Rig*> &rlist) {

	set_title("IC-7410 Rig Control Program (C++ version)");
	set_size_request(1610, 900);

	for(auto r : rlist) {
		myvbox.pack_start( *(new MyDrawingAreaR{r}), FALSE, FALSE, 0);
	}

	for(auto s : slist) {
		myvbox.pack_start( *(new MyDrawingAreaS{s}), FALSE, FALSE, 0);
	}

	add(myvbox);
	show_all();
}

The code looks nice and simple, but the obtained graphical user interface is not very elegant.

Wasted many hours

IC-7410 Rig Control Program (C++ version)_041

For the last few days, some of the widgets appear and disappear randomly after minor modifications of the source, and I could not figure out the reason.

After wasting many hours I finally noticed that some save() and restore() functions do not match.

cr->save();
// some code
cr->restore();

cr->save();
// some code

cr->save();
// some code
cr->restore();

Maybe this style could be better to detect the unmatch manually.

	/* frequency display box */
	{
		cr->save();
		cr->set_source_rgba(0.5+0.2*sin(phase), 0.5+0.2*cos(phase), 0.5, 1.0);
		cr->rectangle(5, 5, 390, 50);
		cr->fill();
		cr->stroke();
		cr->restore();
	}

See that the every block starts with save() and ends with restore().

 egrep '(cr->save()|cr->restore())' *.cpp   
MyDrawingArea2.cpp:	cr->save();
MyDrawingArea2.cpp:	cr->restore();
MyDrawingArea2.cpp:	cr->save();
MyDrawingArea2.cpp:	cr->restore();
MyDrawingArea.cpp:	cr->save();
MyDrawingArea.cpp:	cr->restore();
MyDrawingArea.cpp:	cr->save();
MyDrawingArea.cpp:	cr->restore();
MyDrawingArea.cpp:	cr->save();
MyDrawingArea.cpp:	cr->restore();
MyDrawingArea.cpp:	cr->save();
MyDrawingArea.cpp:	cr->restore();
MyDrawingArea.cpp:	cr->save();
MyDrawingArea.cpp:	cr->restore();

Now, it seems OK.

Soft66LC4 frequency control

The easiest way is to use soft66-control from CLI.

% soft66-control -t 7020000
Tuned to 7020.000kHz

It seems I need another thread to avoid overrun with Alsa Sound System.

void f() {
	system("/usr/local/bin/soft66-control -t 7025400");
}

void g() {
	system("/usr/local/bin/soft66-control -t 7025100");
}

bool MyDrawingArea2::on_draw(const Cairo::RefPtr<Cairo::Context> &cr) {

	static int count = 0;

	if( (count++)%100 == 0) {
		if( (count/100) % 2 == 0) {
			thread t1(f);
			t1.detach();
		} else {
			thread t1(g);
			t1.detach();
		}
	}
}

soft66cont

Since IC-7410 is tuned to 7026.000kHz, a 600Hz tone and a 900Hz are observed alternatively.

Threads in C++11

Maybe I need threads in my rig control program, so here is a simple test.

//======================
// SprigThreadTest.cpp
//======================
#include <iostream>
#include <thread>

void f()
{
    std::cout << "void f()" << std::endl;
}

void g()
{
    std::cout << "void g()" << std::endl;
}

int main(int argc, char **argv)
{
    std::thread t1(f);
    std::thread t2(g);
    t1.join();
    t2.join();
}

Let’s see what happens.

% ./SprigThreadTest 
void g()void f()

% ./SprigThreadTest 
void f()
void g()

% ./SprigThreadTest 
void f()
void g()

% ./SprigThreadTest 
void f()
void g()

% ./SprigThreadTest 
void g()void f()

% ./SprigThreadTest 
void f()
void g()

% ./SprigThreadTest 
void f()
void g()

Looks reasonable? Of course, cout is not thread safe, so we may have interleaved characters.

void f()
{
	for(int i=0;i<10;i++) {
		std::cout << "abcdefghij" << std::endl;
	}
}

void g()
{
	for(int i=0;i<10;i++) {
		std::cout << "0123456789" << std::endl;
	}
}
% ./SprigThreadTest
0123456789
0123456789
0123456789
0123456789
0123456789
0123456789
0123456789
0123456789
0123456789
0123456789
abcdefghij
abcdefghij
abcdefghij
abcdefghij
abcdefghij
abcdefghij
abcdefghij
abcdefghij
abcdefghij
abcdefghij

% ./SprigThreadTest
0123456789abcdefghij
abcdefghij
abcdefghij
abcdefghij
abcdefghij
abcdefghij
abcdefghij
abcdefghij
abcdefghij
abcdefghij

0123456789
0123456789
0123456789
0123456789
0123456789
0123456789
0123456789
0123456789
0123456789

Properties for Sprigmm025 _081

Why isn’t the destructor called at the end of scope?

I was wondering why the destructors of the classes SoundIC7410 and SoundSoft66 are not called at the end of the scope.

int main(int argc, char *argv[]) {
	vector <Sound*> soundlist;
	soundlist.push_back( new SoundIC7410{argv[1]} );
	soundlist.push_back( new SoundSoft66{argv[2]} );

	argc = 1;			/* just for the next line */
	Glib::RefPtr < Gtk::Application > app = Gtk::Application::create(argc, argv, "org.gtkmm.example");
	MyWindow win(soundlist);
	return app->run(win);
}
% ./Rig011 hw:2,0 hw:1,0 /dev/ttyUSB1 &> log.txt
% tail log.txt
Sound::asound_read(): loop_count = 2, frames_actually_read = 2048
Sound::asound_read(): in the while loop, avail = 152

The answer is here:http://www.stroustrup.com/bs_faq2.html#delete-scope

That is, there was some (mistaken) assumption that the object created by “new” would be destroyed at the end of a function.
If you want an object to live in a scope only, don’t use “new” but simply define a variable.

So if I try:

int main(int argc, char *argv[]) {
	vector <Sound*> soundlist;
	SoundIC7410 sound1(argv[1]);
	SoundSoft66 sound2(argv[2]);
	soundlist.push_back(&sound1);
	soundlist.push_back(&sound2);

	argc = 1;			/* just for the next line */
	Glib::RefPtr < Gtk::Application > app = Gtk::Application::create(argc, argv, "org.gtkmm.example");
	MyWindow win(soundlist);
	return app->run(win);
}

We get:

% ./Rig011 hw:2,0 hw:1,0 /dev/ttyUSB1 &> log.txt
% tail log.txt
Sound::asound_read(): loop_count = 2, frames_actually_read = 2048
Sound::asound_read(): in the while loop, avail = 112
SoundSoft66::~SoundSoft66() destructor.. 
SoundIC7410::~SoundIC7410() destructor.. 

Or more simply, try this:

int main() {
	Test  test10, test11;
	Test* test20 = new Test;
	Test* test21 = new Test;
	cout << "test10 = " << &test10 << ", test11 = " << &test11 << endl;
	cout << "test20 = " <<  test20 << ", test21 = " <<  test21 << endl;
	return 0;
}

Test::Test()  { cout << "aaa \n"; }
Test::~Test() { cout << "zzz \n"; }
aaa 
aaa 
aaa 
aaa 
test10 = 0x7ffd56299f50, test11 = 0x7ffd56299f60
test20 = 0x1f8b010, test21 = 0x1f8b030
zzz 
zzz 

Refactoring again

IC-7410 Rig Control Program (C++ version)_038

I am not sure what is the best way to organize the program.

int main(int argc, char *argv[]) {

	vector <Sound*> soundlist;
	soundlist.push_back( new SoundIC7410{argv[1]} );
	soundlist.push_back( new SoundSoft66{argv[2]} );

	vector <Rig*> riglist;
	riglist.push_back  ( new RigIC7410  {argv[3]} );
//	riglist.push_back  ( new RigSoft66  {argv[3]} ); /* dummy */

	argc = 1;			/* just for the next line */
	Glib::RefPtr < Gtk::Application > app = Gtk::Application::create(argc, argv, "org.gtkmm.example");
	MyWindow win(soundlist, riglist);
	return app->run(win);
}
MyWindow::MyWindow(vector <Sound*> &slist, vector <Rig*> &rlist) {

	set_title("IC-7410 Rig Control Program (C++ version)");
	set_size_request(1800, 900);
	myscrolledwindow.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_ALWAYS);
	get_content_area()->pack_start(myscrolledwindow);

	for(auto r : rlist) {
		myvbox.pack_start( *(new MyDrawingArea2{r}), FALSE, FALSE, 0);
	}

	for(auto s : slist) {
		myvbox.pack_start( *(new MyDrawingArea {s}), FALSE, FALSE, 0);
	}

	myscrolledwindow.add(myvbox);
	add(myscrolledwindow);
	show_all();
}

Static members (2)

IC-7410 Rig Control Program (C++ version)_037

Now, I can click on either of the waterfalls to tune into the desired signal. This functionality was gone for several weeks after I started my refactoring. The new feature is the white marker on the spectrum on the lower half to indicate the current VFO frequency of IC-7410.

Unfortunately, the implementation includes the lines which are not very elegant.

	/* show IC-7410 passband on Soft66LC4 waterfall */
	if(nch == 2) {
		cr->save();
		cr->set_source_rgba(0.9, 0.9, 0.9, 0.4);
		cr->rectangle(xspacing + spectrum_x/2 + (AlsaParams::ic7410_frequency-AlsaParams::soft66_frequency)/ s->bin_size - 15, yspacing+(waveform_y+yspacing)*nch, 30, spectrum_y);
		cr->fill();
		cr->stroke();
	}
bool MyDrawingArea::on_button_press_event(GdkEventButton * event) {

	x_press = event->x;
	y_press = event->y;

	int freq;
	switch (nch) {
	case 1: /* IC-7410 */
		if(s->operating_mode == 3) { /* CW is LSB */
			freq = AlsaParams::ic7410_frequency - ( (x_press - xspacing) * s->bin_size - s->cw_pitch );
		} else if(s->operating_mode == 7) { /* CW-R is USB */
			freq = AlsaParams::ic7410_frequency + ( (x_press - xspacing) * s->bin_size - s->cw_pitch );
		} else {
			;
		}
		break;
	case 2: /* Soft66LC4 */
		freq = AlsaParams::soft66_frequency + ( (x_press - xspacing) - (waterfall_x / 2) ) * s->bin_size;
		break;
	default:
		return false;
	}
	Sound::set_ic7410_frequency(freq);
	return true;
}

Of course I can use virtual functions to eliminate all such if or switch statements, but I am not very sure if I should.

The exact position and the size of the white marker should reflect the bandwidth of IC-7410, and if it is in LSB or USB mode, but now they are both fixed.