How do I appropriately call constructors and pass objects around? - c++

I have the following code, but something is wrong with it. It compiles, but nothing happens on the NeoPixel matrix. I've loaded up a strand test to verify that the hardware is working properly. Through some manual debugging, I've worked out that the line containing PixelArt art = PixelArt(matrix); triggers the issue. If I comment that out, I get a panel of orange light coming out of the light matrix as expected. Putting it back in simply results in darkness.
What about including that one line is causing issues? I'm new to C++ and this is driving me up the wall.
For some code context, I plan on including more modes, each with a series of submodes. PixelArt would show off different pixel art pictures as its sub modes. Other modes would include different animation patterns. The different modes should all share a reference to the same matrix object.
#include <Adafruit_NeoMatrix.h>
#include <gamma.h>
#define MODE_PIN 2
#define SUB_MODE_PIN 3
#define MATRIX_PIN 9
#define MATRIX_COLUMNS 16
#define MATRIX_ROWS 16
#define MATRIX_NUM_PIXELS (MATRIX_COLUMNS * MATRIX_ROWS)
class Mode {
public:
Mode(Adafruit_NeoMatrix &neomatrix) : matrix{&neomatrix} {}
virtual void toggleSubMode() = 0;
virtual void update(uint64_t timestamp) = 0;
protected:
Adafruit_NeoMatrix * const matrix;
};
class PixelArt : public Mode {
public:
PixelArt(Adafruit_NeoMatrix &neomatrix):Mode(neomatrix) {}
void toggleSubMode();
void update(uint64_t timestamp);
};
void PixelArt::toggleSubMode() {
// Stubbed.
}
// Green: 0x80c000
// Brown: 0xc04000
// Tan: 0xffa040
uint32_t link[16][16] = { [REDACTED FOR BREVITY] };
void PixelArt::update(uint64_t timestamp) {
matrix->clear();
for (uint16_t row = 0; row < 16; row++) {
for (uint16_t column = 0; column < 16; column++) {
matrix->setPassThruColor(link[row][column]);
matrix->drawPixel(column, row, 0);
matrix->setPassThruColor();
}
}
matrix->show();
}
//
Adafruit_NeoMatrix matrix = Adafruit_NeoMatrix(
MATRIX_COLUMNS,
MATRIX_ROWS,
MATRIX_PIN,
NEO_MATRIX_TOP | NEO_MATRIX_LEFT | NEO_MATRIX_COLUMNS | NEO_MATRIX_ZIGZAG,
NEO_GRB | NEO_KHZ800
);
PixelArt art = PixelArt(matrix);
void setup() {
matrix.begin();
matrix.show();
matrix.setBrightness(30); // Max ~80
}
uint8_t mode = 0;
void loop() {
// art.update(millis());
matrix.fillScreen(matrix.Color(255, 127, 0));
matrix.show();
}

My suggestion would be that because the code runs on a microcontroller, it may run out of memory. Especially if the target board is an Arduino Uno or similar, with small dynamic memory. After compilation, the memory usage calculation is done for compile-time variables. The run-time variables and function calls may exceed the available memory.
Compiled for an Arduino Uno, the compiler calculates a 1204 bytes (58%) of dynamic memory usage with PixelArt object. Without it is 168 bytes (8%).
Try reducing the inheritance and function call levels.

Related

Using Wire.onRequest by passing a class method?

I have an Arduino sketch that will be working on an Arduino UNO and I am trying to get uno to communicate over the i2c connection with a raspberry pi.
Problem is using wire.h library where method Wire.onRequest is working just fine when I use it like this.
#include <Wire.h>
#define COMM_DELAY 50
#define SLAVE_ADDRESS 0x04
int current_rule = 0;
void initI2c() {
// initialize i2c as slave
Wire.begin(SLAVE_ADDRESS);
// define callbacks for i2c communication
Wire.onReceive(receiveData);
}
// callback for received data
void receiveData(int byteCount) {
while (Wire.available()) {
current_rule = Wire.read();
}
}
but when I try to make this exact result with a class method, I get an error :
invalid use of non-static member function
(with Wire.onRequest(this->receiveData) line gets to be marked red)
Just like this:
void (*funptr)();
typedef void (*Callback)(byte);
class Comm{
public:
int callback_list_size = 0;
bool option_debug;
byte option_address;
int option_comm_delay;
void(*callback_list[256]);
byte *rules;
// function for receiving data. raspberry -> arduino
// Whenever the master sends new data, this method will call the appropriate callback.
void receiveData()
{
byte data;
Serial.println("[INFO] Received new data from master");
while (Wire.available())
{
data = Wire.read();
}
for (int i = 0; i < callback_list_size; i++)
{
if (rules[i] == data){
funptr = callback_list[i];
funptr();
}
}
}
// function for sending data. Called when raspberry request data. arduino -> raspberry
// Whenever the master requests data, this method will be called. For now we don't need this but anyway.
void sendData(int s)
{
if (option_debug)
Serial.println("[INFO] Master requests data!");
}
/* Constructor that takes 3 parameters at max. Only the adress is mandatory others are optional and will be filled with default values
:address - adress of slave(arduino) - Example 0x04
:delay - a delay is needed because I2C clock is quite slow compared to the CPU clock - 50
:debug - for debug purposes if true debug info will be sent to Serial interface - true/false
*/
Comm(byte address, int delay = 50, bool debug = false)
{
option_address = address;
option_comm_delay = delay;
option_debug = debug;
if (debug)
Serial.println("[INFO] Comm Object Created!");
}
// Function needs to be called to initialize the communication channel.
void initI2c()
{
Wire.begin(option_address);
Wire.onReceive(this->sendData);
Wire.onRequest(this->receiveData);
if (option_debug)
Serial.println("[INFO] I2C channel initialized");
}
// Function to add new callback for a rule.
// This function returns id of passed callback
int addCallback(Callback func, byte rule)
{
callback_list_size++;
// Enlarge rules array to keep 1 more byte
byte *temp = new byte[callback_list_size]; // create new bigger array.
for (int i = 0; i + 1 < callback_list_size; i++) // reason fo i+1 is if callback_list_size is 1 than this is the first initializition so we don't need any copying.
{
temp[i] = rules[i]; // copy rules to newer array.
}
delete[] rules; // free old array memory.
rules = temp; // now rules points to new array.
callback_list[callback_list_size - 1] = &func;
rules[callback_list_size - 1] = rule;
return callback_list_size;
}
};
Comm *i2c_comm;
void loop()
{
}
void setup()
{
Serial.begin(9600);
initI2C();
}
void initI2C()
{
i2c_comm = new Comm(0x04, 50, true);
i2c_comm->initI2c();
//Callback Definitions
i2c_comm->addCallback(&rule_1, 0x01);
i2c_comm->addCallback(&rule_2, 0x02);
i2c_comm->addCallback(&rule_3, 0x03);
i2c_comm->addCallback(&rule_4, 0x04);
}
I also tried to make the receiveData method to be static.
But in this case I have an error like this:
invalid use of member Com::callback_list_size in static member function
which makes sense to me as static method won't know which callback_list_size I am talking about.
so I am quite confused about how I can handle such a problem?
You're almost there. Generally speaking in C++ you need to pass a static class method for callback functions.
The error you received after changing your method to static is expected as you're trying to access a member of an instance of the class Comm which cannot be done in a static method in which there is no 'this'.
Here's one of many techniques to consider, but please read over the SO post Using a C++ class member function as a C callback function.
Anyway the approach here is to leverage a static pointer to an instance.
class Comm {
private:
static Comm* pSingletonInstance;
static void OnReceiveHandler() {
if (pSingletonInstance)
pSingletonInstance->receiveData();
}
static void OnSendHandler(int s) {
if (pSingletonInstance)
pSingletonInstance->sendData(s);
}
void initI2c() {
Comm::pSingletonInstance = this; // Assign the static singleton used in the static handlers.
Wire.onReceive(Comm::OnSendHandler);
Wire.onRequest(Comm::OnReceiveHandler);
Wire.begin(option_address);
}
}
// static initializer for the static member.
Comm* Comm::pSingletonInstance = 0;
Again there are many ways to get around this issue but above is an easy one and likely suitable for your project. If you need to manage multiple instances of Comm, you'll have to do something quite different.
Good luck!

C++ referencing instances created within a function's scope

Context
The context of the problem is that I am currently writing a small library for use with the Arduino in order to act as a game controller. The problem I am encountering has more to do with C++ than anything Arduino specific however.
I've included the libraries' header and source code below, followed by the Arduino code. I've truncated it where possible.
Problem
In short, only the last switch / action I define actually gets properly handles.
These actions get defined in the Arduino setup function. For example:
controller.addSwitchContinuous(10, 0); // Pin 10; btn index 0
means that pin 10 gets mapped to button 0. When pin 10 is switched closed this is treated as the button being pressed. This works fine for a single action but when I start adding more only the last action actually works. So in the following example only pin 9 is recognized:
controller.addSwitchContinuous(10, 0); // <-- Doesn't work
controller.addSwitchContinuous(9, 1); // <-- Works
This goes for any arbitrary number of actions:
controller.addSwitchContinuous(10, 0); // <-- Doesn't work
controller.addSwitchContinuous(9, 1); // <-- Doesn't work
controller.addSwitchContinuous(8, 2); // <-- Doesn't work
controller.addSwitchContinuous(7, 3); // <-- Works
Potential causes
I am fairly novice with C++ so this I suspect I'm doing something wrong with pointers. More specifically, something seems wrong with how the Joystick_ instance gets passed around.
I have been fiddling with the constructor and trying to use references instead of pointers but I couldn't get it to work properly.
I can confirm the iteration in JFSF::loop does iterate over all actions, if I modify it with:
void JFSF::loop()
{
for (int n = 0; n < _nextActionIndex; n++)
{
if (_actions[n])
{
_actions[n]->loop();
_joystick->setButton(n, PRESSED); // Debug: Set button pressed, regardless of switch.
}
}
if (_doSendState)
{
_joystick->sendState();
}
}
then buttons 0 through n get pressed as expected. It is possible that loop() isn't properly being called, but I would expect it to fail for the N = 1 case as well in that case. Furthermore the fact the last action always succeeds would suggest the iteration is ok.
Full code
// JFSF.h
#ifndef JFSF_h
#define JFSF_h
// ... include for Arduino.h and Joystick.h; bunch of defines
namespace JFSF_PRIV
{
class AbstractAction
{
public:
virtual void loop();
};
/* A Switch that essentially acts as a push button. */
class SwitchContinuousAction : public AbstractAction
{
public:
SwitchContinuousAction(Joystick_ *joystick, int pin, int btnIndex);
void loop();
private:
Joystick_ *_joystick;
int _pin;
int _btnIndex;
};
} // namespace JFSF_PRIV
class JFSF
{
public:
JFSF(Joystick_ *joystick, bool doSendState); // doSendState should be true if Joystick_ does not auto send state.
void loop();
void addSwitchContinuous(int inputPin, int btnIndex);
private:
Joystick_ *_joystick;
JFSF_PRIV::AbstractAction *_actions[MAX_ACTIONS];
int _nextActionIndex;
bool _doSendState;
};
#endif
Source file (trimmed):
// JFSF.cpp
#include "Arduino.h"
#include "Joystick.h"
#include "JFSF.h"
#define PRESSED 1
#define RELEASED 0
// Private classes
namespace JFSF_PRIV
{
SwitchContinuousAction::SwitchContinuousAction(Joystick_ *joystick, int pin, int btnIndex)
{
_joystick = joystick;
_pin = pin;
_btnIndex = btnIndex;
pinMode(_pin, INPUT_PULLUP);
}
void SwitchContinuousAction::loop()
{
int _state = digitalRead(_pin) == LOW ? PRESSED : RELEASED;
_joystick->setButton(_btnIndex, _state);
}
} // namespace JFSF_PRIV
JFSF::JFSF(Joystick_ *joystick, bool doSendState)
{
_joystick = joystick;
_nextActionIndex = 0;
_doSendState = doSendState;
}
void JFSF::addSwitchContinuous(int inputPin, int btnIndex)
{
JFSF_PRIV::SwitchContinuousAction newBtnAction(_joystick, inputPin, btnIndex);
_actions[_nextActionIndex++] = &newBtnAction;
}
void JFSF::loop()
{
for (int n = 0; n < _nextActionIndex; n++)
{
if (_actions[n])
{
_actions[n]->loop();
}
}
if (_doSendState)
{
_joystick->sendState();
}
}
For completeness sake, this is the code for the Arduino, but it is pretty much just declarations:
#include <JFSF.h>
// ... A bunch of const declarations used below. These are pretty self explanatory.
// See: https://github.com/MHeironimus/ArduinoJoystickLibrary#joystick-library-api
Joystick_ joystick(HID_REPORT_ID,
JOYSTICK_TYPE_JOYSTICK, // _JOYSTICK, _GAMEPAD or _MULTI_AXIS
BTN_COUNT, HAT_SWITCH_COUNT,
INCLUDE_X_AXIS, INCLUDE_Y_AXIS, INCLUDE_Z_AXIS,
INCLUDE_RX_AXIS, INCLUDE_RY_AXIS, INCLUDE_RZ_AXIS,
INCLUDE_RUDDER, INCLUDE_THROTTLE,
INCLUDE_ACCELERATOR, INCLUDE_BRAKE, INCLUDE_STEERING);
JFSF controller(&joystick, !DO_AUTO_SEND_STATE);
void setup() {
joystick.begin(DO_AUTO_SEND_STATE);
controller.addSwitchContinuous(10, 0); // <-- Doesn't work
controller.addSwitchContinuous(9, 1); // <-- Works
}
void loop() {
controller.loop();
}
References
ArduinoJoystickLibrary (Source for Joystick_) can be found here: https://github.com/MHeironimus/ArduinoJoystickLibrary#joystick-library-api
I dont really understand your code. Please read How to create a Minimal, Complete and Verifiable example. Anyhow, the following is certainly wrong and likely the cause of your problem:
void JFSF::addSwitchContinuous(int inputPin, int btnIndex)
{
JFSF_PRIV::SwitchContinuousAction newBtnAction(_joystick, inputPin, btnIndex);
_actions[_nextActionIndex++] = &newBtnAction;
}
Lets rewrite it a bit for clarity:
void foo(){
T bar;
container[index] = &bar;
}
What happens here is that bar gets destroyed when it goes out of scope, hence the pointer you put into the container, points to garbage. Presumably somewhere else in your code you are dereferencing those pointers, which is undefined behaviour (aka anything can happen).
Long story short: It is a common pattern among c++ beginners to overuse pointers. Most likely you should make container a container of objects rather than pointers and make use of automatic memory managment instead of trying to fight it.
Thanks to #user463035818 and #drescherjm for identifiying the actual problem.
So in the end I fixed it by simply moving the Action object creation up to the Arduino code (where it's essentially global) and passing references to those objects to the controller.
In code this translates to:
JFSF.cpp
void JFSF::addAction(JFSF_PRIV::AbstractAction *action){
_actions[_nextActionIndex++] = action;
}
Arduino code (ino)
// See code in original post
JFSF controller(&joystick, !DO_AUTO_SEND_STATE);
JFSF_PRIV::SwitchContinuousAction btnOne(&joystick, 10, 0);
JFSF_PRIV::SwitchContinuousAction btnTwo(&joystick, 9, 1);
void setup() {
joystick.begin(DO_AUTO_SEND_STATE);
// controller.addSwitchContinuous(10, 0); // Pin 10; btn index 0
// controller.addSwitchContinuous(9, 1); // Pin 9 ; btn index 1
controller.addAction(&btnOne);
controller.addAction(&btnTwo);
}
// loop() is unchanged

c/c++ get large size data like 180 array from another class in stm32

I have an 32-bit ARM Cortex M4 (the processor in Pixhawk) to write two classes, each one is one threading in Pixhawk codebase setting.
The first one is LidarScanner, which dealing with incoming serial data and generates "obstacle situation". The second one is Algorithm, which handle "obstacle situation" and take some planning strategy. Here are my solution right now, use the reference function LidarScanner::updateObstacle(uint8_t (&array)[181]) to update "obstacle situation" which is 181 size array.
LidarScanner.cpp:
class LidarScanner{
private:
struct{
bool available = false;
int AngleArr[181];
int RangeArr[181];
bool isObstacle[181] = {}; //1: unsafe; 0:safe;
}scan;
......
public:
LidarScanner();
//main function
void update()
{
while(hal.uartE->available()) //incoming serial data is available
{
decode_data(); //decode serial data into three kind data: Range, Angle and Period_flag
if(complete_scan()) //determine if the lidarscanner one period is completed
{
scan.available = false;
checkObstacle(); //check obstacle situation and store safety in isObstacle[181]
scan.available = true;
}
}
}
//for another API recall
void updateObstacle(uint8_t (&array)[181])
{
for(int i=0; i<=181; i++)
{
array[i]=scan.isObstacle[i];
}
}
//for another API recall
bool ScanAvailable() const { return scan.available; }
......
}
Algorithm.cpp:
class Algorithm{
private:
uint8_t Obatcle_Value[181] = {};
class LidarScanner& _lidarscanner;
......
public:
Algorithm(class LidarScanner& _lidarscanner);
//main funcation
void update()
{
if (hal.uartE->available() && _lidarscanner.ScanAvailable())
{
//Update obstacle situation into Algorithm phase and do more planning strategy
_lidarscanner.updateObstacle(Obatcle_Value);
}
}
......
}`
Usually, it works fine. But I want to improve the performances so that I want to know what's the most effective way to do that. thanks!!!!
The most efficient way to copy data is to use the DMA.
DMAx_Channelx->CNDTR = size;
DMAx_Channelx->CPAR = (uint32_t)&source;
DMAx_Channelx->CMAR = (uint32_t)&destination;
DMAx_Channelx->CCR = (0<<DMA_CCR_MSIZE_Pos) | (0<<DMA_CCR_PSIZE_Pos)
| DMA_CCR_MINC | DMA_CCR_PINC | DMA_CCR_MEM2MEM ;
while(!(DMAx->ISR & DMA_ISR_TCIFx ));
AN4031 Using the DMA controller.

(C++) Need help making splash screen for reprapdiscount smart controller 4x20 LCD display - EDITED

I recently purchased a delta 3d printer kit that uses the marlin software, MKS mini (kossel) main board (uses Arduino bootloader & Marlin Firmware), and reprapdiscount smart controller LCD display (4 rows x 20 characters).
I have almost zero programming experience, and I would like some help to create a "splash screen" that is called on boot only, then goes back to the status screen after a certain time. I have already scoured the internet on how to do this, but the only ones that had any viable options were for the FULL GRAPHICS version LCD display - which is not what I have. I have found the .cpp file used for this specific LCD display - however with my limited programming knowledge, I cannot figure out how to add a splash screen on start.
I'm pretty sure that I have to create a void function at the beginning of the menu implementation (see below), but i'm not sure what / how to call it so that it starts first, then switches to the status screen afterwards. I can write the pseudo code for it, but don't know the full code...
side-note: I just realized that in the menu items, it shows the reference variable of the text, which is actually contained in another file called language.h
PSEUDO CODE of what I would like it to do:
//start->boot printer
static void splashscreen()
{
/*splashlines are defined in language.h file*/
static void splashline1(); // hello
static void splashline2(); // world
static void splashline3(); // i'm
static void splashline4(); // here!
wait 3 seconds;
switch to -> static void lcd_status_screen();
}
Any help would be greatly appreciated!
Unfortunately the post is limited to 30,000 characters, and posting the original code will put me at over 50,000... so I will try to post
relevant code snippets:
THANKS IN ADVANCE!!
EDITED
~ LCD status screen code ~
#ifdef ULTIPANEL
static float manual_feedrate[] = MANUAL_FEEDRATE;
#endif // ULTIPANEL
/* !Configuration settings */
//Function pointer to menu functions.
typedef void (*menuFunc_t)();
uint8_t lcd_status_message_level;
char lcd_status_message[LCD_WIDTH+1] = WELCOME_MSG;
/** forward declerations **/
void copy_and_scalePID_i();
void copy_and_scalePID_d();
/* Different menus */
static void lcd_status_screen();
#ifdef ULTIPANEL
extern bool powersupply;
static void lcd_main_menu();
static void lcd_tune_menu();
static void lcd_prepare_menu();
static void lcd_move_menu();
static void lcd_control_menu();
static void lcd_control_temperature_menu();
static void lcd_control_temperature_preheat_pla_settings_menu();
static void lcd_control_temperature_preheat_abs_settings_menu();
static void lcd_control_motion_menu();
~ EDITED Start of coding ~
menuFunc_t currentMenu = lcd_status_screen; /* function pointer to the currently active menu */
uint32_t lcd_next_update_millis;
uint8_t lcd_status_update_delay;
uint8_t lcdDrawUpdate = 2; /* Set to none-zero when the LCD needs to draw, decreased after every draw. Set to 2 in LCD routines so the LCD gets atleast 1 full redraw (first redraw is partial) */
//prevMenu and prevEncoderPosition are used to store the previous menu location when editing settings.
menuFunc_t prevMenu = NULL;
uint16_t prevEncoderPosition;
//Variables used when editing values.
const char* editLabel;
void* editValue;
int32_t minEditValue, maxEditValue;
menuFunc_t callbackFunc;
// placeholders for Ki and Kd edits
float raw_Ki, raw_Kd;
/* Main status screen. It's up to the implementation specific part to show what is needed. As this is very display dependend */
static void lcd_status_screen()
{
if (lcd_status_update_delay)
lcd_status_update_delay--;
else
lcdDrawUpdate = 1;
if (lcdDrawUpdate)
{
lcd_implementation_status_screen();
lcd_status_update_delay = 10; /* redraw the main screen every second. This is easier then trying keep track of all things that change on the screen */
}
#ifdef ULTIPANEL
if (LCD_CLICKED)
{
currentMenu = lcd_main_menu;
encoderPosition = 0;
lcd_quick_feedback();
}
// Dead zone at 100% feedrate
if ((feedmultiply < 100 && (feedmultiply + int(encoderPosition)) > 100) ||
(feedmultiply > 100 && (feedmultiply + int(encoderPosition)) < 100))
{
encoderPosition = 0;
feedmultiply = 100;
}
if (feedmultiply == 100 && int(encoderPosition) > ENCODER_FEEDRATE_DEADZONE)
{
feedmultiply += int(encoderPosition) - ENCODER_FEEDRATE_DEADZONE;
encoderPosition = 0;
}
else if (feedmultiply == 100 && int(encoderPosition) < -ENCODER_FEEDRATE_DEADZONE)
{
feedmultiply += int(encoderPosition) + ENCODER_FEEDRATE_DEADZONE;
encoderPosition = 0;
}
else if (feedmultiply != 100)
{
feedmultiply += int(encoderPosition);
encoderPosition = 0;
}
if (feedmultiply < 10)
feedmultiply = 10;
if (feedmultiply > 999)
feedmultiply = 999;
#endif//ULTIPANEL
}
As Joel Cornett suggested, look up the gaps in knowledge and ask specifically about those gaps. In that case, it seems what you need is the following:
How to code a timed pause for the "splash" to display.
Where to initialize the code you want (I think you may have overlooked it, as I don't think see it in the code you posted).
How to make the code proceed to the status screen without looping on itself.
Try to edit your post / split your questions into individual posts. You may get a better response rate from that.

GNU Radio: How to define a "get_*" method inside a sink block

I'd like to code my own sink block with 1 input port and 0 output ports for GNU Radio in C++. I read and followed the steps described here:
http://gnuradio.org/redmine/projects/gnuradio/wiki/BlocksCodingGuide
http://gnuradio.org/redmine/projects/gnuradio/wiki/OutOfTreeModules
http://gnuradio.org/redmine/projects/gnuradio/wiki/OutOfTreeModulesConfig
http://gnuradio.org/redmine/projects/gnuradio/wiki/Guided_Tutorial_GNU_Radio_in_C++
I'm using
Ubuntu 14.04.3 LTS
Eclipse 4.5.1
CDT 8.8.0.201509131935
GNU Radio 3.7.8
Using gr_modtool I created the new module "jammertrap". Inside it, I created the block "bandpower". This created among others the three files
bandpower.h inside /home/sdr/gnuradio/gr-jammertrap/include/jammertrap/
bandpower_impl.h inside /home/sdr/gnuradio/gr-jammertrap/lib/
bandpower_impl.cc inside /home/sdr/gnuradio/gr-jammertrap/lib/
bandpower.h:
#ifndef INCLUDED_JAMMERTRAP_BANDPOWER_H
#define INCLUDED_JAMMERTRAP_BANDPOWER_H
#include <jammertrap/api.h>
#include <gnuradio/block.h>
namespace gr
{
namespace jammertrap
{
class JAMMERTRAP_API bandpower : virtual public gr::block
{
public:
typedef boost::shared_ptr<bandpower> sptr;
// Return a shared_ptr to a new instance of jammertrap::bandpower.
// To avoid accidental use of raw pointers, jammertrap::bandpower's constructor is in a private implementation class.
// jammertrap::bandpower::make is the public interface for creating new instances.
static sptr make();
};
} // namespace jammertrap
} // namespace gr
#endif /* INCLUDED_JAMMERTRAP_BANDPOWER_H */
bandpower_impl.h:
#ifndef INCLUDED_JAMMERTRAP_BANDPOWER_IMPL_H
#define INCLUDED_JAMMERTRAP_BANDPOWER_IMPL_H
#include <jammertrap/bandpower.h>
namespace gr
{
namespace jammertrap
{
class bandpower_impl : public bandpower
{
private:
double d_bandpower_;
public:
bandpower_impl();
~bandpower_impl();
void forecast (int noutput_items, gr_vector_int &ninput_items_required);
// Where all the action really happens
int general_work(int noutput_items, gr_vector_int &ninput_items, gr_vector_const_void_star &input_items);
// Returns the calculated RMS Bandpower
double get_bandpower();
};
} // namespace jammertrap
} // namespace gr
#endif /* INCLUDED_JAMMERTRAP_BANDPOWER_IMPL_H */
bandpower_impl.cc:
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <gnuradio/io_signature.h>
#include "bandpower_impl.h"
namespace gr
{
namespace jammertrap
{
bandpower::sptr
bandpower::make()
{
return gnuradio::get_initial_sptr (new bandpower_impl());
}
// The private constructor
bandpower_impl::bandpower_impl() : gr::block("bandpower", gr::io_signature::make(1, 1, sizeof(gr_complex)), gr::io_signature::make(0, 0, 0))
{
d_bandpower_ = 0;
}
// Our virtual destructor
bandpower_impl::~bandpower_impl()
{}
void bandpower_impl::forecast(int noutput_items, gr_vector_int &ninput_items_required)
{
// ninput_items_required[0] = noutput_items;
}
int bandpower_impl::general_work(int noutput_items, gr_vector_int &ninput_items, gr_vector_const_void_star &input_items)
{
const gr_complex *in = (const gr_complex *) input_items[0];
d_bandpower_ = 0;
for(int i = 0; i < noutput_items; i++)
{
d_bandpower_ += (in[i].real() * in[i].real()) + (in[i].imag() * in[i].imag());
}
d_bandpower_ = sqrt(d_bandpower_ / noutput_items);
// Tell runtime system how many input items we consumed on each input stream.
consume_each (noutput_items);
// Tell runtime system how many output items we produced
return noutput_items;
}
double bandpower_impl::get_bandpower()
{
return d_bandpower_;
}
} /* namespace jammertrap */
} /* namespace gr */
To make and install this new block, I entered the following commands inside /home/sdr/gnuradio/gr-jammertrap/build:
cmake ../
make
make test // Result: "test_jammertrap" and "qa_bandpower" passed successful
sudo make install
This sink block "bandpower" should receive items of type gr_complex, compute the average received power and store this value inside the private member "d_bandpower_".
Additional I defined the method "get_bandpower()" to get the stored value.
Inside another program, I created a flowgraph class with the two blocks
osmosdr::source:sptr osmosdr_source_;
gr::jammertrap::bandpower::sptr bandpower_measurement_;
and instanced them with
osmosdr_source_ = osmosdr::source::make(std::string());
bandpower_measurement_ = gr::jammertrap::bandpower::make();
After starting the flowgraph, I want to read the calculated bandpower by calling get_bandpower() but Eclipse shows no method "bandpower_measurement_->get_bandpower()"
What have I forgotten to write inside bandpower.h, bandpower_impl.h or bandpower_impl.cc?
The public API of the normal OOT layout is in the bandpower.h, so you must add a
virtual double get_bandpower() = 0;
in that file.
Then, you overload/implement that, like you do, in the _impl.cc/_impl.h.
By the way, I slightly object the math behind your implementation: as noutput_items, ie. the number of input items available, changes depending on buffer fillage / runtime behaviour, your "averaging length" is not constant, which means that if your flow graph runs fast, your buffers will usually be full, and your averaging length high, whilst in a "trickle" situation, the length will be much smaller (down to noutput_items==1, in extreme cases). Hence, the variance of your power estimator will depend on computational aspects.
That's not a good thing. Better work with a constant number of items you average about. In your case, you can use set_output_multiple (because a sink is also a sync block, this also affects the input multiples) to guarantee you always get a multiple of a fixed number.
Other than that, there's already blocks that can do what you want:
Probe Avg Mag²: has a method level() which does the same as your get_bandpower (aside from the √(.) )
Complex to Mag ⟶ Decimating FIR Filter ⟶ Probe Signal: does the √(Re²+Im²), before passing it on to a filter with 123 (that was just my arbitrary fixed length) taps of 1/length, and decimating it to one value every average. Result get send to the Signal Probe, which has a signal() method, which does what your get_bandpower does. CPU load of this is relatively small -- it's really just the magnitude finding for every sample, and then 123 real multiplications + 123 real additions per 123 samples,in the filter, all SIMD increased, so basically, less than 1 FMAC per sample.
Complex to Mag ⟶ Moving Average ⟶ Probe Signal: Names say it all. Nothing magical here.