Friday, December 9, 2022

C++: Using constant for array size

In C, you can use a constant when declaring an array:

  const n = 5;
  int a[n];

In C++, it would result in compile error "constant expression required". You have to declare it using constexpr:

  constexpr int n = 5;
  int a[n];

However, if you for example want to use the size of a vector to declare an array, you can't use the stack, you have to use the heap:

  int* a = new int[myVector.size()];
  ...
  delete[] a;

Tuesday, November 1, 2022

Formatting disk from USB

I recently needed to wipe a laptop's disk. I used Rufus and Puppy Linux (ISO size was 409MB) to create a bootable USB drive. After booting into Puppy Linux, I used the following commands:
  1. lsblk to see disk partitions and their sizes
  2. lsblk -f to see partition file systems
  3. sudo mkfs -t ntfs /dev/<partition>
Formatting a disk (initializing with zeros) of 500GB can take 3 hours.

Wednesday, October 26, 2022

Formatting C++ code in Visual Studio

C++ code is usually formatted as follows:
if (condition)
{

}
else {

}

I like the curly braces to be on the same line so that I can see more code in less lines:
if (condition) {

} else {

}

To define this format in Visual Studio 2022, go to Tools - Options - Text Editor - C/C++ - Code Style - Formatting - New Lines and uncheck the boxes at the bottom:
After this change, you can update the format of an existing file with Edit - Advanced - Format Document.

If you also want the code snippet for if to behave similarly, go to Tools - Code Snippet Manager - Visual C++, copy the location of the if snippet file, open the file in a text editor with admin privileges and edit and save it:


Tuesday, October 25, 2022

Initial values in Java and C++

In Java a variable of type int or double has the initial value of zero. But in C++, they can get random values, as you can see in the example below. Note that in C++, the easiest way to set all fields to zero is to use memset.
#include <iostream>
typedef struct {
    int n;
    double values[12];
} DATA;

int main() {
    DATA data;
    std::cout << data.n << ", " << data.values[3] << std::endl;
    memset(&data, 0, sizeof(data)); //set all fields to zero
    std::cout << data.n << ", " << data.values[3] << std::endl;
    std::cout << "Press enter..." << std::endl;
    std::cin.get();
}

Friday, October 14, 2022

Recursive and iterative nested for loops

Consider n nested for loops in Python:

for i_1 in range(n):
    for i_2 in range(n):
        for i_3 in range(n):
            ...
                for i_n in range(n):
                    doSomething()

The recursive way would be:

def nForLoops(n, depth = 0):
    if depth > n-1: doSomething()
    else:
        for i in range(n):
            nForLoops(n, depth + 1)

A simple iterative function could be written using the observation that doSomething() is called n^n times:

def nForLoops2(n, counter = 0):
    while counter < pow(n, n):
        counter += 1
        doSomething()

TODO: How could we write it iteratively using stack data structure?

Wednesday, October 5, 2022

Reducing maintenance effort

You should strive to design simulations that require minimal maintenance. The ideal is to create a simulation whose only maintenance work is proving that it is working correctly when there is a problem in the system of which your simulation is part of. A good way of reducing the proof effort is to have unit tests with good coverage and a developer handbook containing frequently asked questions and troubleshooting sections. Of course, you cannot reach this goal at the beginning, you will get closer to it with every iteration.

Thursday, September 1, 2022

Different results when rounding floating points

Java's Math.round(0.5) and C++'s std::round(0.5) result in the value of 1 while Python 3 round(0.5) results in zero because while Java and C++ use round up, Python 3 uses round half to even.

Thursday, July 28, 2022

Dealing with legacy projects

One of the areas I provide value is improving the quality of legacy projects, see this, this, this, and this. Yesterday, I was asked again for help with a legacy project that contains both hardware and software but no documentation, i.e. it used heroic development. Here are the steps we should follow:
  1. Convince me: Why are you asking for my help, why don't you continue as before? Are you trying to please the quality department with documentation fluff? Where did you get stuck, what are the concrete issues you think I can help? Are there any standards that must to be followed or are these standards nice to have?
  2. Interview the developers and create a document on Confluence, write what problem the project solves, what its current status is, what the current problems are and what more features will be added. Add a diagram showing the hardware and software components and their interfaces, especially external interfaces. Later on you can add more details, making it a proper developer handbook.
  3. Let the developers do a live demonstration of the current capabilities.
  4. Upload code and tools necessary for build to a repository.
  5. On a clean PC, build code. Add repository links and build steps to document.
  6. Add unit tests starting with the simplest sections. Update document as you add more and more tests and become acquainted with the code/design.
  7. Add automated nightly build scripts, update document.
  8. Add steps of hardware assembly and test to document.
  9. If there are manual tests, create a test plan document detailing test steps on Confluence. It should include links to documents on how to assemble and test the hardware and how to build the code and upload to hardware.
  10. Create a deployment document (or add to existing document) detailing the steps of how the product is delivered to the end user and how user will share issues/requests with the developers.

Recovering an USB drive

After creating an Antix linux operating system installation USB drive (30GB) and finishing the OS installation, I wanted the use the drive as usual for copying files. However, Windows 10 showed me a full 4MB drive and did not allow me to copy any files there:
I opened Windows Disk Management a saw that there was an unallocated 29.95GB volume:
I right clicked on the unallocated partition but New Simple Volume option was disabled. I ran Windows diskpart.exe and selected disk 1:
I deleted the partition on disk 1 using the override flag:

Now in Disk Management, New Simple Volume option was enabled:

I selected that option and created an new volume but formatting failed with "The system cannot find the file specified". When I tried to format with file explorer, I got "Windows was unable to complete the format" message. With diskpart.exe, I used clean:
In Disk Management I tried New Simple Volume again, this time it formatted the drive successfully:

Wednesday, June 29, 2022

Stack overflow on Linux

...exceeding the stack limit is usually considered a segmentation violation, and systems with enough memory management to detect it will send a SIGSEGV [segmentation fault] when it happens.

A typical symptom in a C++ program running on Linux is getting a segmentation fault when entering an innocent function like pow(). To debug, decrease stack usage (e.g. if there is a static array, decrease its size) in the code before the segfault, run your program in debug mode, see if your program continued further than before. Unfortunately, the same program might be working on Windows without problems.

To increase stack size on Linux, use ulimit -s <size_KB>

Thursday, June 23, 2022

Benefits of wiki style documentation

Wiki style (no signatures/approvals) documentation of complex software projects (lines of code > 10K) has the following benefits:

  1. Wiki approach saves you from wasting time in publication and approval process. It enables quick updates which increases quality of content.
  2. You as the developer will be able to remember important details of design, especially if long time has passed since you last worked on it.
  3. You can hand-off the project to junior developers without wasting your time.
  4. You can easily extract a user manual from existing content and users won't bother you with questions.
  5. Code reviewers get a better idea of overall design which increases the quality of review comments.

Wednesday, June 22, 2022

Java: Table with numeric input and length checks

Sample code showing how to do numeric input and character length checks with a JTable:


Tuesday, June 21, 2022

Stop build when "C4013 ... undefined; assuming extern returning int"

Recently I had to deal with a bug in a C project whose root cause was fabs not functioning properly due to missing #include <math>. Finding the root cause involved diving into more than 4 layers of abstractions, it was not fun (!) When run from Visual Studio as a dll project, the code resulted in fabs(10) >= fabs(20) to be true! When I compiled the project with Visual Studio, it gave "warning C4013: 'fabs' undefined; assuming extern returning int". I would normally expect the build to fail in the linking stage. Probably the linker is able to find the fabs but since it was first assumed to be returning int (4 bytes) and fabs returns double (8 bytes), this will result in 4 bytes of the return value being ignored, which can cause funny values. For more about the reason why this behavior is allowed, see my similar trouble with malloc before.

Another interesting fact is that the code was working properly when run from a Simulink s-function. I assume Simulink includes math.h somewhere in its hierarchy. Here is the simplified code:

To prevent Visual Studio from building when 4013 warning exists, go to project Properties - C/C++ - Advanced - Treat Specific Warnings as Errors - Add 4013:



Wednesday, June 15, 2022

String input to Matlab C Mex file

When you call a Matlab C Mex function with a string input, make sure to use single quotes, e.g. 'hello'. If you use double quotes, e.g. "hello", Matlab will crash and burn!

Monday, May 16, 2022

Dangers of C++

Recently I had a strange bug that caused a previously working C++ simulation to fail. It turned out that a logically unrelated portion of code was corrupting the memory and that corruption resulted in changing of parameter values which resulted in instability. Corruption was due to writing out of index. If that index pointed to a memory location outside the boundaries of my program I would get an access violation. Unfortunately, indices were pointing to my programs memory, therefore I did not get any errors from the operating system and had to pin point the bug by trial and error, i.e. commenting out sections of code until I got a stable state and then uncommenting until I got instability. A simplified version of the code:

Friday, May 13, 2022

Simulink: Generating code without model version and date

When you generate code with Simulink, the generated header and source files will have model version and date in code comments. This results in version changes in your version control system even when you have not changed any logic. In order to avoid unnecessary version updates, you should comment out the "Model version" and "C/C++ source code generated on" sections in the ert_code_template.cgt file (Configuration Parameters > Code Generation > Templates > Code Templates).

Dangerous while loop

Today a program I am writing stopped responding. After some debugging effort, I came across a 3rd party function that limited an input angle to the [-180, 180] degrees interval. It was using a while loop to increment or decrement the angle. Unfortunately, I was passing an uninitialized variable to it and its value was -9.2...e+61. Such a large value would take years for the function to limit. Below is the original function constrainAngleWhile(), together with much better alternatives:

Wednesday, April 27, 2022

What does valgrind's "still reachable" message mean?

When I check my programs with valgrind for memory leaks I usually get a "still reachable" result, even if there is no memory leak. An example valgrind output:

HEAP SUMMARY:

    in use at exit: 72,704 bytes in 1 blocks

   total heap usage: 3 allocs, 2 frees, 74,752 bytes allocated

72,704 bytes in 1 blocks are still reachable in loss record 1 of 1

LEAK SUMMARY:

   definitely lost: 0 bytes in 0 blocks

   indirectly lost: 0 bytes in 0 blocks

   possibly lost: 0 bytes in 0 blocks

   still reachable: 72,704 bytes in 1 blocks

On valgrind's doc it says: "still reachable" means your program is probably ok -- it didn't free some memory it could have. This is quite common and often reasonable.

I wrote a simple program:

#include <iostream>

int main() {

    return 0;

}

After I compile and check it with valgrind I get "still reachable". If I comment out the #include line, I don't get "still reachable". I have to research a little bit more.

Wednesday, April 20, 2022

C++ "already defined" error

One of the most frustrating errors that a beginner C++ developer faces is the "already defined" linker error. Below is an example that can cause that error and its three solutions:

For more information, see includeinline and static keywords.

Sunday, March 20, 2022

GitHub: Go back to a previous commit

 You can see commit history by clicking the commits link:



You copy the SHA of the commit you want to revert to, open a terminal, go to source folder and type

git reset --hard <SHA>

Tuesday, March 15, 2022

C++: Dynamically create array of objects

Dynamically creating array of object on the heap:

MyClass** objects;

arrSize = 100;

objects = new MyClass*[arrSize]; //Note the '*' before arrSize

for (size_t i = 0; i < arrSize; i++) {

objects[i] = new MyClass();

}

Freeing up heap memory allocated to object array:

for (size_t i = 0; i < arrSize; i++) {

delete objects[i];

}

delete[] objects;

Tip: You can use Visual Studio's Memory Usage diagnostic tool to check heap memory changes which gives clues about memory leaks. To get the function names where memory is allocated but then never released, use valgrind on WSL (Linux) as follows:

valgrind --tool=memcheck --leak-check=yes ./<app name>

Sunday, March 13, 2022

Generating Linux programs in Windows Visual Studio C++

You can use Windows 10 and Visual Studio 2022 to generate, debug and analyze Linux binaries:

  1. Install WSL, restart computer
  2. Open cmd, type wsl to enable linux prompt, install tools
    1. sudo apt-get update
    2. sudo apt install g++ gdb make ninja-build rsync zip
  3. Open Visual Studio, on menu Tools - Get Tools and Features, add "Linux development with C++" if you haven't done already:
  4. In Visual Studio, 
    1. Create new project and select CMake project:
    2. After project is created, change Local Machine to WSL:Ubuntu. Change Startup item to your project name:
    3. If Visual Studio notifies you that CMake needs to be updated/installed, let it do it.
    4. Press debug button to build and run:
    5. At the bottom of the screen, in Output window, change it to CMake:
    6. Visual Studio tells you where it has generated the Linux binary. Copy this folder to clipboard:
    7. Go to View - Terminal. In the opened PowerShell terminal at the bottom, type wsl and press enter, the prompt will change to linux :
    8. Type cd and paste the folder you copied:
    9. Now you can also run your Linux binary from terminal:
    10. To use valgrind, first install it in PowerShell by typing sudo apt install valgrind
    11. Now you can use valgrind on your binary without leaving Visual Studio by typing (replace ./CMakeProject4 with your own project name): valgrind --tool=memcheck --leak-check=yes ./CMakeProject4:
    12. To copy the binary to a Windows folder, you can open File Explorer from Linux prompt by typing explorer.exe .:
    13. Congratulations!

Thursday, March 10, 2022

C++: Using arrays with threads

The following C++ code demonstrates how to safely use an array that is written to by one thread and read from in another thread:

Tuesday, March 8, 2022

Java: Get raw text in JFormattedTextField

You could define a custom class that derives from JFormattedTextField and use KeyListener to get the raw text that the user is typing:

Monday, March 7, 2022

Linux cheat sheet

Linux commands I frequently use:
  1. Change to root (on Ubuntu): sudo -i
  2. uname -r 
  3. sudo apt update && sudo apt upgrade
  4. Show processes whose name contains a specific string: ps -ef | grep <string>
  5. Forcefully kill process: pkill -f <process name>
    • Example: pkill -f update-notifier
  6. Run process in background: ./<process name> &
  7. Show network card info: ifconfig
  8. Show resource usage of running processes: top, htop
  9. Reverse-i-search with ctrl+r
  10. Change MAC address: sudo ifconfig enp2s0 hw ether 64:00:6a:28:fa:ac
  11. Ubuntu: Allow GUI root login
  12. Enable SSH to root user by adding the line PermitRootLogin yes to /etc/ssh/sshd_config
  13. Enable receiving UDP packets 
    1. Disable firewall completely: sudo ufw disable
    2. Or you can allow for example on port 5000: sudo ufw allow from any to any port 5000 proto udp
  14. Create image of USB with size of 8GB: dd if=/dev/sdb of=/home/myimage.img bs=1G count=8 status=progress
Tools:
  1. sudo apt install net-tools
  2. sudo apt install build-essential
    • gcc --version
    • make --version
  3. Eclipse IDE for C++
    1. In a project, when you remove a folder and copy another version of the same folder but with fewer files, during build, you might get the error "No rule to make target…". Right click on the project and select refresh, this rebuilds the index. Now you can build successfully.
  4. To use psftp from another Windows computer for file transfer: sudo apt install openssh-server
  5. sudo apt install rt-tests
    • Sample usage: cyclictest l100000 -t 8 p95
  6. sudo apt install htop
  7. For remote connection from Windows: 
    • sudo apt install xrdp
    • sudo systemctl restart xrdp
    • Note that you have to be logged out from Ubuntu for remote from Windows to work.

Wednesday, February 23, 2022

Running a process from C++

 When you want to run a separate process from C++:

Wednesday, February 9, 2022

Vertical Landing Rocket Competition

This year's Teknofest has a Vertical Landing Rocket Competition. We can divide the control problem into two:

  1. Stabilize orientation so that the thruster points in downward direction.
  2. Control vertical speed to achieve a soft landing.
Orientation can be stabilized via passive fins or active side jets. Since there isn't much time for manufacturing and testing, using fins seems to be the easiest choice.
Assuming that the fins achieve close to zero yaw and pitch, speed can be controlled by changing the thrust via valve control using PID.  I wrote a Matlab script that can be used as a starting point:
I also wrote a simple open loop code that has no control, you can find it here.

Sunday, January 30, 2022

Installing C++17 on CentOS 7 and using it with Eclipse

By default, CentOS 7 supports C++ up to 2011 (C++11). For C++17 (2017) support with Eclipse IDE:
  1. Login to CentoOS 7 as root.
  2. Open terminal.
  3. Install Developer Toolset 8yum install dev-toolset-8
  4. cd into Eclipse folder.
  5. Enable toolset 8 for Eclipse and open it: scl enable devtoolset-8 ./eclipse
  6. After Eclipse opens, open/create a C++ project, use this filesystem example.
  7. Go to project properties - C/C++ Build - Settings - Tool Settings - GCC C++ Compiler -  Dialect - Other dialect flags and enter -std=c++17:
  8. Now you can build your project using C++ 2017 features. If you print __cplusplus, you get 201703. Note that project include folders point to devtoolset-8:

Rebuild index. Now you can compile but still get linker error.

Add linker flag -lstdc++fs and reorder linker flags so that -lstdc++fs is at the end: ${COMMAND} ${OUTPUT_FLAG} ${OUTPUT_PREFIX}${OUTPUT} ${INPUTS} ${FLAGS}

Now you can successfully run the example.

Tuesday, January 25, 2022

Development phases of aerospace flight software

When developing embedded software for a complex system, it is not advisable to do the development primarily on target hardware because a test that might take a single developer 5 minutes on a PC might take 4 people half a day on target hardware. We are talking about 100x of efficiency difference. To minimize time spent on target hardware, the following six phases of aerospace flight software development might help:

This approach converts the typical time - effort curve A to a more manageable B:
Phase 1: Test application logic of each software configuration item separately on non real time commercial PCs  (communication is only via ethernet). To do this you have to have abstraction layers for hardware, OS, time synchronization and non-ethernet interfaces. Examples of application logic: Checking flight envelope, reading DTED files, calculating checksum, converting IMU mathematical model outputs to specific IMU brand interface, estimating position. Tests should be automated and run every night. Automated tests immensely reduce the stress of refactoring and experimentation because if you have "good enough" test coverage, any problem will be discovered at most a day later.

Phase 2: Use commercial PCs with real time OS, integrate software components with each other (still only ethernet communication) and verify that software interfaces and state transitions that depend on timing work as expected. Again, use automated nightly tests.

Phase 3: Now that you have high confidence in software due to thousands of tests having passed during the previous phases, begin deploying software on electronics hardware and test. At this stage, you should mainly focus on verifying that software is working in the constrained environment of target hardware (small heap, stack, disk, RAM, processing power etc.) and that non-ethernet interfaces are ok. If there are problems in application logic, go back to Phase 1/2 development environment for debugging.

Phase 4: Deploy all software to hardware and test.

Phase 5: Add other electro-mechanical systems (sensors, actuators) and test on the ground.

Phase 6: Do full system flight test.

Saturday, January 15, 2022

C++: const_cast use case

A third party function that I have to use has an input of type char* which the function does not modify. Normally the type should be const char* but the function writer was a novice it seems. I cannot change it to const char* because it is used in other code that such a change would break. I have a variable that needs to be string so that I can use the flexibility of string. In order to send that string variable into the third party function, I cannot just use  string.c_str() because the compiler would complain that you cannot send a const char* to a function that expects char*. To remove the "constness", I have to use const_cast<char*> (string.c_str()). Example implementation:

Note that the third party function does not modify the input char*. If it did, I could not use const_cast (even tough it would compile) because it would result in undefined behaviour.

Friday, January 14, 2022

File search in Eclipse and Visual Studio

 To search for files in

  • Eclipse: ctrl + shift + r
  • Visual Studio: ctrl + , (comma)
    • It also searches for methods and classes

Monday, January 10, 2022

Eclipse working folder

To change Eclipse working folder so that file paths are the same when running project from IDE and when running the binary from terminal, you have to set the working directory (similar to Visual Studio) using the Run Configurations - Arguments - Working Directory: ${workspace_loc:MyProject/Debug}

Note that Eclipse run configurations are saved in <eclipse-workspace>.metadata/.plugins/org.eclipse.debug.core/.launches

To copy files on Linux Eclipse in post-build step:

  1. Select Project - Properties - C/C++ Build - Settings - Build Steps
  2. In Post-build steps, enter cp source destination
If you have changed the working folder as explained above, then source will be relative to that working folder. If, for example, you want to copy a folder to working folder, you could use cp -f -R ../src/config .
You can enter more than one cp command by separating them with a semicolon (;)

Sunday, January 9, 2022

xcopy and Eclipse build path variables

When working with Eclipse on Windows, some of the system build path variables will have "/" while others have "\" as path separator:


When using xcopy in Post-build steps, there cannot be "/" in path because xcopy treats anything with a "/" as a parameter. For example if you use ${ProjDirPath} with xcopy, you will get "invalid number of parameters" error. To solve it, edit ProjDirPath and replace "/" with "\". Don't forget to do it for both Debug and Release configurations.

Wednesday, January 5, 2022

Checking C++ version in code

If your code has to behave differently for different C++ versions, you can check the value of __cplusplus directive. It will return the year and month of C++ standard used in your current environment. Examples:

  • 199711L stands for Year=1997, Month = 11
  • 201103L stands for Year=2011, Month = 03
If you want to run code only if C++ version is less than C++11, you write:
#if __cplusplus < 201100L
When you use Visual Studio 2019 and print the value of __cplusplus to console, you will see 199711, although in the project properties - C/C++ - Language page it is set to C++14:
For Visual Studio to output the correct value, you have to go to Configuration Properties > C/C++ > Command Line and add /Zc:__cplusplus. Now you get the correct value of 201402.

Tuesday, January 4, 2022

Visual Studio Setup

The default Visual Studio setup is impractical. Here is my setup for C++:

  • Output Directory: $(SolutionDir)bin\$(Platform)\$(Configuration)\
  • Intermediate Directory: $(SolutionDir)bin\intermediate\$(Platform)\$(Configuration)\
  • To read files from exe folder instead of solution folder when running from IDE, Working Directory: $(SolutionDir)bin\$(Platform)\$(Configuration)\

  • I put a sample VS2022 project template to github.
  • To copy an input file (file.txt) to exe folder after a build: xcopy $(SolutionDir)file.txt $(SolutionDir)bin\$(Platform)\$(Configuration)\ /Y

Note: If you use the same intermediate folder for all configurations, you might get "fatal error C1041: cannot open program database ...vc120.pdb". The solution is to set /FS command line flag.