Static members

Selection_035

The upper half of the area is for IC-7410, and the lower for Soft66LC4. The VFO frequencies are displayed, nameley, 7026.000kHz for IC-7410 amd 7020.000kHz for Soft66LC4.

When we click on the waterfall in the Soft66LC4 area, we want to change the VFO freqeuncy of IC-7410. So we need to get and set variables that do not belong to a class instance from which we want to manipulate. One method could be:

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

	s->asound_read();
	s->asound_fftcopy();
	fftw_execute(s->plan);

	int freq1 = s->get_frequency();
	int freq2 = s->get_other_frequency();

	char string[128];
	sprintf(string, "%9.3fkHz  %9.3fkHz", (double)freq1 / 1000.0, (double)freq2 / 1000.0);
}
class Sound : public AlsaParams {
public:
	virtual int  get_frequency      () = 0;
	virtual int  get_other_frequency() = 0;
	int  get_ic7410_frequency() const { return ic7410_frequency; }
	int  get_soft66_frequency() const { return soft66_frequency; }
}
class SoundIC7410: public Sound {
public:
	int  get_frequency  () override;
	int  get_other_frequency () override { return get_soft66_frequency(); }
}
class SoundSoft66: public Sound {
public:
	SoundSoft66(char* s, char* t);
	int  get_frequency       () override;
	int  get_other_frequency () override { return get_ic7410_frequency(); }
}
struct AlsaParams  {
	static int ic7410_frequency;
	static int soft66_frequency;
}

Redundant? Which style do you prefer?

//	int  get_other_frequency () override { return get_soft66_frequency(); }
	int  get_other_frequency () override { return soft66_frequency; }

How do you name variables?

During refactoring my code, it took me some time before I realized why I was having the compilation errors.

class MyDrawingArea : public Gtk::DrawingArea {
public:
    //
private:
    Sound* s = nullptr;
};

MyDrawingArea::MyDrawingArea(Sound* ss) : s {ss} {
    nch = s->get_channels(); // O.K.
}

You agree with me that the short names, such as s or ss, are elegant than longer ones, but:

bool MyDrawingArea::on_draw(const Cairo::RefPtr<Cairo::Context> &cr) {
    s->asound_read(); // O.K.
    // many lines here
    for(int ix=0;ix<waveform_x;ix++) {
        cr->line_to(ix, s->audio_signal[ix];) // Error!
    }

I first thought it was OK with member functions, but not with data members, and I was checking only class definitions.

Actually, it was due to the following lines moved from another place and inserted at //many lines here:

//{   /* braces are here before, but deleted later to beautify (!) the code */
    unsigned char *s;
    s= buf;
    for(i=0;i<buf_len;i++) {
        cout << *s++;
    }
// }

http://www.stroustrup.com/bs_faq2.html#Hungarian

https://google-styleguide.googlecode.com/svn/trunk/cppguide.html#Variable_Names

Rig Control recovering slowly

rigcont

Functionally, still quite limited, but the source code is now much easier to read by refactoring, I suppose.

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

	vector <Sound*> soundlist;
	soundlist.push_back( new SoundIC7410{argv[1], argv[3]} );
	soundlist.push_back( new SoundSoft66{argv[2], nullptr} ); /* no rig control yet */

}
/* Sound.h */

#ifndef SOUND_H_
#define SOUND_H_

#include "AlsaParams.h"
#include "RigControlParams.h"
#include "RigControlIC7410.h"
#include <asoundlib.h>

class Sound : public AlsaParams, public RigControl {
public:
	Sound();
	virtual int get_channels   () const = 0;
	virtual int get_nfft       () const = 0;
	virtual int get_spectrum_x () const = 0;
	virtual int get_spectrum_y () const = 0;
	virtual int get_waterfall_x() const = 0;
	virtual int get_waterfall_y() const = 0;
	virtual int get_timervalue () const = 0;
	virtual int get_index(int, int, int) const = 0;
	virtual bool get_smeter() const = 0;
	virtual int asound_fftcopy () = 0;
	int asound_init();
	int asound_go();
	int asound_read();
	int asound_set_hwparams();
	int asound_set_swparams();
	virtual ~Sound();
};

#endif /* SOUND_H_ */

What is a pure virtual function? (2)

So to generate the index to an array, instead of doing:

for(int ix=0;ix<spectrum_x;ix++) {
    if(nch == 1) { /* IC7410 */
        ixx = ix;
    } else if(nch == 2) { /* Soft66LC4 */
        ixx = (2*nfft - (spectrum_x/2) + ix) % nfft;
    }
    // other stuff
}

we do:

for(int ix=0;ix<spectrum_x;ix++) {
    ixx = s->get_index(ix, nfft, spectrum_x);
    // other stuff
}
class Sound : public AlsaParams { /* abstract base class */
public:
    virtual int get_index(int, int, int) const = 0;
    // other stuff
};
class SoundIC7410: public Sound { /* IC7410 */
public:
    int get_index(int i, int, int) const override { return i; }
    // other stuff
};
class SoundSoft66: public Sound { /* Soft66LC4 */
public:
    int get_index (int i, int j, int k) const override { return (2*j-(k/2)+i)%j; }
    // other stuff
};

I do not know which style looks better and is more maintainable.

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

The window is now scrollable again.

//============================================================================
// Name        : Rig008.cpp
//============================================================================

#include "SoundIC7410.h"
#include "SoundSoft66.h"
#include "MyDrawingArea.h"
#include "MyWindow.h"
#include <gtkmm.h>
#include <vector>
#include <iostream>
using namespace std;

int main(int argc, char *argv[]) {
	if (argc != 3) {
		cout << "Usage example: " << argv[0] << " hw:2,0 hw:1,0 " << endl;
		return 1;
	}

	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);
}
/* MyWindow.h */

#ifndef MYWINDOW_H_
#define MYWINDOW_H_

#include "Sound.h"
#include <gtkmm.h>
#include <vector>
using namespace std;

class MyWindow : public Gtk::Dialog {
public:
	MyWindow(vector <Sound*> &slist);
	virtual ~MyWindow();
private:
  Gtk::ScrolledWindow myscrolledwindow;
  Gtk::VBox           myvbox;
};

#endif /* MYWINDOW_H_ */
/* MyWindow.cpp */

#include "MyWindow.h"
#include "MyDrawingArea.h"

MyWindow::MyWindow(vector <Sound*> &slist) {
	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 s : slist) {
		myvbox.pack_start( *(new MyDrawingArea{s}), FALSE, FALSE, 0);
	}
	myscrolledwindow.add(myvbox);
	add(myscrolledwindow);
	show_all();
}

MyWindow::~MyWindow() {
}

https://github.com/jh1ood/sprigmm/tree/develop

What is a pure virtual function?

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

https://isocpp.org/wiki/faq/virtual-functions#pure-virtual

I removed almost all of my if and/or switch statements using pure virtual functions.

/* Sound.h */

#ifndef SOUND_H_
#define SOUND_H_

#include "AlsaParams.h"
#include <asoundlib.h>;

class Sound : public AlsaParams {
public:
	Sound();
	virtual int get_channels   () const = 0;
	virtual int get_nfft       () const = 0;
	virtual int get_spectrum_x () const = 0;
	virtual int get_spectrum_y () const = 0;
	virtual int get_waterfall_x() const = 0;
	virtual int get_waterfall_y() const = 0;
	virtual int asound_fftcopy () = 0;
	int asound_init();
	int asound_go();
	int asound_read();
	int asound_set_hwparams();
	int asound_set_swparams();
	virtual ~Sound();
private:
	int count = 0;
};

#endif /* SOUND_H_ */
/* SoundIC7410.h */

#ifndef SOUNDIC7410_H_
#define SOUNDIC7410_H_

#include "AlsaParams.h"
#include "Sound.h"

class SoundIC7410: public Sound {
public:
	SoundIC7410(char *s);
	int get_channels   () const override { return channels; }
	int get_nfft       () const override { return     nfft; }
	int get_spectrum_x () const override { return      512; }
	int get_spectrum_y () const override { return       50; }
	int get_waterfall_x() const override { return      512; }
	int get_waterfall_y() const override { return      150; }
	int asound_fftcopy() override;
	virtual ~SoundIC7410();
};

#endif /* SOUNDIC7410_H_ */

There is now only one such statement here:

void f(Sound *s){
	nch = s->get_channels();
	for(int ix=0;ix<spectrum_x;ix++) {
		if(nch == 1) { /* IC7410 */
			ixx = ix;
		} else if(nch == 2) { /* Soft66LC4 */
			ixx = (2*nfft - (spectrum_x/2) + ix) % nfft;
		}
		// draw spectrum and waterfall here
	}
}

The if statement is here because I wish to set the DC to the left edge or to the center depending on the signals. Of course, I can write functions for each derived class, like:
ixx= s->g(ix);
but, I do not know if I should.

Refactoring (3)

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

Now recovered the waterfall windows.

The program is here: https://github.com/jh1ood/sprigmm/tree/develop

The total number of lines is about 740, and I believe there is almost no redundancy in the code.

 % wc *.h *.cpp
   45   156  1151 AlsaParams.h
   35    95   693 MyDrawingArea.h
   29    63   465 Sound.h
   23    49   388 SoundIC7410.h
   25    54   429 SoundSoft66.h
  184   654  4409 MyDrawingArea.cpp
   39   106  1040 Rig008.cpp
  258   992  7194 Sound.cpp
   53   201  1292 SoundIC7410.cpp
   51   212  1349 SoundSoft66.cpp
  742  2582 18410 total

Refactoring (2)

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

Added fft windows. (No waterfall windows yet.)

It seems that the code looks much better now, I mean more C++ like.

//============================================================================
// Name        : Rig008.cpp
//============================================================================

#include "SoundIC7410.h"
#include "SoundSoft66.h"
#include "MyDrawingArea.h"
#include <gtkmm.h>
#include <vector>
#include <iostream>
using namespace std;

int main(int argc, char *argv[]) {
	if (argc != 3) {
		cout << "Usage example: " << argv[0] << " hw:2,0 hw:1,0 " << endl;
		return 1;
	}

	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");

	Gtk::Window   mywindow;
	Gtk::VBox     mybox;
	mywindow.set_title("IC-7410 Rig Control Program (C++ version)");

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

	mywindow.add(mybox);
	mywindow.show_all();

	return app->run(mywindow);
}

We have two different types of sound devices (see lines 20-21), IC7410 (I channel only) and Soft66 (I and Q channels).

But in the for loop (see lines 30-32), we just use a pointer s to the base class Sound, which is an abstract class with pure virtual functions.

/* Sound.h */

#ifndef SOUND_H_
#define SOUND_H_

#include "AlsaParams.h"
#include <asoundlib.h>

class Sound : public AlsaParams {
public:
	Sound();
	virtual int get_channels() const = 0;
	virtual int asound_fftcopy() = 0;
	virtual ~Sound();
};

#endif /* SOUND_H_ */

The functions in the two derived classes, SoundIC7410 and SoundSoft66, override these virtual functions so that the different characteristics of the devices are treated properly.

/* SoundIC7410.h */

#ifndef SOUNDIC7410_H_
#define SOUNDIC7410_H_

#include "AlsaParams.h"
#include "Sound.h"

class SoundIC7410: public Sound {
public:
	SoundIC7410();
	SoundIC7410(char *s);
	int get_channels() const override { return 1; }
	int asound_fftcopy() override;
	virtual ~SoundIC7410();
};

#endif /* SOUNDIC7410_H_ */
/* SoundIC7410.cpp */

#include "SoundIC7410.h"
#include <iostream>
using namespace std;

SoundIC7410::SoundIC7410() {
}

SoundIC7410::SoundIC7410(char* s) {
	sound_device = s;
	channels = 1;
	rate = 32000;
	buffer_size = 32 * 1024;
	period_size =  8 * 1024;
	nfft = 4 * 1024; /* IC-7410 */
	samples            = new signed short[period_size * channels * 2];
	audio_signal       = new double      [period_size * channels * 2];
	audio_signal_ffted = new double [nfft];
	in_real = new double[nfft];
	out  = (fftw_complex *) fftw_malloc(sizeof(fftw_complex) * ( nfft/2 + 1 )*2 );
	plan = fftw_plan_dft_r2c_1d(nfft, in_real, out, FFTW_MEASURE);
	asound_init();
	asound_go();
}

int SoundIC7410::asound_fftcopy() {
	for (int i = 0; i < nfft; i++) {
		in_real[i] = audio_signal[i];
	}
	return 0;
}

SoundIC7410::~SoundIC7410() {
}

The important part is that there are no if nor switch statements to take care of the different types of devices.