Thanks for your interest in making Pitot better! Here are some useful information that will help you get started in developing for Pitot.
The majority of Pitot's logic has been written in Rust, and chances are you need to learn some amount of Rust to be able to contribute to Pitot.
Unfortunately Rust is not known for having the shallowest learning curve, meaning you may need to spend some time learning before being able to understand all of Pitot's code. However, there are no shortage of good resources on helping you achieving that. Here are some of my favorites:
- Rust by Example. This can get you started with Rust relatively quickly.
- The Rust Programming Language. You may need to refer to this book often while writing Rust code.
- Official Rust Documentations Collection. Including links to the above two.
Very much unlike Stratux, Pitot uses a modular design that intends to abstracts away the knowledge of other components running in the system. As a result, you can replace/remove any components inside Pitot without causing trouble elsewhere. This is fundamental for supporting more hardware/protocol/feature as the project grows bigger.
Pitot primarily has four stages, and higher stage uses information from lower stage to complete it's tasks (just like a turbine engine). From bottom to top, we have Sensor stage, Processor stage, Protocol stage and finally, Transport stage. I will explain what each of those stages does below.
Sensor stage is how Pitot acquires data from the outside world. SDR interface, GNSS interface and IMU/baro interface should all be categorized as sensor component and should be running inside this stage.
Sensor stage takes the raw sensor reading and generates pitot::sensor::SensorData
, which is an enum
of multiple possible
parsed sensor reading. SensorData
will get passed to the Processor stage for further processing.
Processor stage is how Pitot analyzes sensor input and maintain/generates state information about the world around it.
Processors read SensorData
, find out the ones they are interested in, and optionally update their internal state
as necessary.
Processor stage will output pitot::processor::Report
enum
using their internal state either periodically or under
some determined condition. Report
will get passed to the Protocol stage for further processing.
Protocol stage is how Pitot converts Report
into output protocol format, for example, GDL90 and JSON. Protocol
stage generally does not have states (although it is possible to).
Protocol stage will output pitot::protocol::Payload
struct
. Payload
will get passed to the Transport stage
for final processing.
Transport stage is how Pitot outputs the Payload
from Protocol stage using output interface. For example, UDP
or serial.
Transport stage may have state information. For example, a UDP transport need to maintain the list of all clients it needs to send to.
Transport stage does not generate any more data in the Pitot processing pipeline as it is the last stage of the process.
Main loop is what keeps everything in Pitot running. It runs at a fixed frequency (10 Hz at this moment, but do not hardcode this in your code!) and went through all modules in all stages from lowest stage to highest stage in a single thread.
Main loop also facilitates message passing from lower stage to higher stage.
It is critical that your module does NOT use any blocking operation while running inside the main loop. Doing so will significantly downgrade the performance of Pitot and will cause Pitot to output warning messages in log.
If you need to run blocking operation, you should run them in a separate thread and passes messages back to the main loop in an asynchronous manner. See the UAT/1090 ES code for an example of this in action.
For each module, it will receive all messages pushed by the stage directly below it. Modules uses the pattern matching feature of Rust to filter out the ones it's interested in and ignore all others.
The current Pitot architecture has been working well for me, although I did had to redesign it at some point to make it more generalized. That being said, there is still a small possibility the architecture may change, if we found new drawbacks about it.
Ok, now you have gained some basic understanding on how Pitot operates, let's get started on developing!
Note: despite being the same name, the libdump978
Pitot uses is NOT the same thing as libdump978
Stratux uses. It runs more efficiently by avoiding copying of raw I/Q samples from Pitot to
the library and also have a very different API interface.
Dependencies: you need libusb-dev
and librtlsdr-dev
installed for those build below to succeed.
$ git clone [email protected]:dndx/dump978.git
$ cd dump978
$ git checkout libdump978
$ make
$ sudo make install
$ git clone [email protected]:dndx/dump1090.git
$ cd dump1090
$ git checkout libdump1090
$ make
$ sudo make install
After that, make sure your system recognizes the newly installed shared libraries by running:
$ sudo ldconfig
and you should be good to go!
Make sure to have the appropriate shared library installed before attempting to build Pitot
as rustc
will attempt to dynamically link against those libraries and you may got linker
error if they can not be found.
Luckily, Rust is pretty easy to setup, simply go to the Install Rust page and follow the instructions, you should be up and running in minutes.
Building Pitot is extremely simple! Once you have cloned the repository, simply run
$ cargo build
and this will build Rust in debug mode. The binary file will be located under pitot/target/debug/pitot
.
If you want to build Pitot in release mode instead, do:
$ cargo build --release
The binary file will be located under pitot/target/release/pitot
.
Debug mode turns off all compile time optimizations and turns on a lot of assertions which will make Pitot run slower. However, during the early stage of development, I recommend you use debug mode to help catching bugs and make Pitot better!
$ cargo test
Pitot has a test set that is still improving. We encourage you run Pitot on every change you make and of course to contribute tests for existing features and features you have developed. Having a good test is how we will ensure the consistent quality of Pitot as the project goes forward.
If you have noticed and test failure on your machine, please open an issue with the output and we will be sure to take a look.
Pitot can be debugged using GDB, Valgrind and the built in debug logs. To see the logs, run debug Pitot manually like this:
$ RUST_BACKTRACE=1 RUST_LOG=trace path/to/pitot
The RUST_LOG
parameter takes values from trace
all the way up to error
. Check out the
documentation of env_logger
for more information.
When running Pitot on your Raspberry Pi build as system service, you may run sudo journalctl -u pitot
to view logs. Note that logs are not persistent due to the fact that root file system is readonly.
Pitot can run it's tests perfectly fine on x86 machines, but for it to actually work in the cockpit, you need to make it run on your actual Pitot build. Here is how you do it:
First, build an ARM Pitot binary using either an Raspberry Pi directly or cross-compiling. I personally have a separate Raspberry Pi hooked on my home network for this purpose. It is generally easier to setup than cross-compiling.
Next, SSH into Pitot
$ ssh [email protected]
# password is "raspberry", no quotes
You can verify that readonly protection is active by running:
$ df -h
and observe /dev/mmcblk0p2
is mounted as /mnt/root-ro
.
Now, disable readonly protection on the root partition by editing
/boot/cmdline.txt
and append disable-root-ro=true
to the last
and reboot.
If you run df -h
again, /dev/mmcblk0p2
will no longer be mounted as
/mnt/root-ro
and you can write to your root partition now.
Stop the Pitot process by running:
$ sudo systemctl stop pitot
and copy the new binary to overwrite /usr/local/bin/pitot
.
Next, very important, you need to give Pitot capability to change system time and to use raw socket:
$ sudo setcap CAP_SYS_TIME,CAP_NET_RAW+ep /usr/local/bin/pitot
Once you are done, remove the addition from /boot/cmdline.txt
and reboot.
Finally, verify your root partition is mounted readonly again by running
df -h
and check for the output.
If you need any help while developing, feel free to open a GitHub Issue and I will try my best to take a look.
- ADS-B Decoding Guide
- Technical Provisions for Mode S Services and Extended Squitter
- ICAO Annex 10 Volume IV Surveillance and Collision Avoidance Systems