PPM garbage detection

I am using an analog, 75 Mhz FM radio on a submarine build. VHF penetrates under water, unlike 2.4 GHz.

I found the PPM signal on the RX PCB where it comes out of the RF section, and when I connect it to RCIN, I get beautiful RC inputs in ArduPilot.

My problem is that if the receiver loses signal (e.g., if I switch off the TX), I get complete garbage as RC input; values jump wildly. It makes sense. The RF section is outputting noise, and trying to interpret noise as PPM produces garbage. I wonder if there is any provision in the AP PPM decoder to filter out noise like this, and report lost signal instead.

I guess most use cases for PPM in AP involve PPM from digital receivers that generate it locally from a binary over-the-air stream. It must be uncommon to use raw analog input :=)

Edit 2023-10-01: Here are some traces of valid PPM and noise

These traces show analog output of the RX’s RF section, a digital representation of it (what RCIN would see), and how the RX interprets the first three channels. RX outputs servo PWM pulses only when it thinks it has valid input.

I think it is much easier to use a PPM multiplexer at the receiver pulse outputs, such as this:
and see what happens at the PPM or SBUS outputs when you switch off the transmitter.

This seems like software noise rejection, but with extra steps. To be sure, the little Atmel in the RX is doing exactly that, rejecting bad PPM frames. This was a huge improvement over old setups that simply had a shift register that used to twitch servos randomly without valid RF signal.

The techniques they use are fairly straightforward:

  • make sure number of channels is consistent between frames
  • make sure PPM polarity is consistent between frames
  • make sure complete pulse widths (rising-edge-to-rising-edge) are consistently within limits
  • make sure marker pulse widths (rising-edge-to-falling-edge) are consistent
  • possibly limit slew rates

RXs and demultiplexers do this in code. AP can do this in code without extra hardware.

I looked at the AP code that handles PPM, and it’s quite liberal in what it accepts as valid PPM. I may have to make a PR to tighten it up. Interestingly, PPM decoder never reports failsafe under any conditions, even when it rejects frames.

I would suggest you build a decoder on a dedicated microcontroller (like an Arduino) to replicate the above:
** make sure number of channels is consistent between frames*
** make sure PPM polarity is consistent between frames*
** make sure complete pulse widths (rising-edge-to-rising-edge) are consistently within limits*
** make sure marker pulse widths (rising-edge-to-falling-edge) are consistent*
** possibly limit slew rates*

And optimize the code so you can have a working demonstrator to submit a PR for a “special branch” of ArduPilot. I might not be possible to merge as it is a very special use case.

Thank you for your suggestion, @ppoirier . If I make a PR, it will come with unit tests. I’m curious about two things in your suggestion. One, what is the benefit of first compiling the code on a standalone uC, and on an incompatible architecture at that. Two, what makes this hypothetical PR more special than other PRs? Clearly, analog PPM is uncommon in this world, but I generally go out of my way to avoid breaking existing, desirable behavior. It would add about a uint8_t’s worth of memory demand, and there’s already a macro to compile PPM support out if you don’t need it. OTOH, I’d be very curious to see a legitimate use case of a digital PPM signal that reports different number of channels from one frame to the next.


Dedicated microcontroller is just a proof of concept, making the code integration easier.

Glad to see a fellow submariner at this forum - before switching to 868 MHz recently I ran my subs @ 40 MHz for many years with a Futaba FX30 and a diversity RX system (ACT) - I generated the PPM sum signal for the flight controller by connecting individual RX servo outputs (signal only) to a Rmilec V3 signal converter - the converter lets you you choose between PPM or SBUS output - and transmitts RX failsafe settings - never had any problems with erratic servo movements.

Hi @U-Boat! I mostly do fixed-wing. I have 5 currently-airworthy planes with ArduPilot. This is my first successful submarine. I’m very curious to hear your experience with sub-GHz UHF underwater. Maybe I can replace the 75 MHz with 900 and be done with this.

sure - glad to share success and failures - to advice you I need a few details on the boats hardware and the diving environment: is it a static or a dynamic diver? Did you buy a specific sub kit or are you going to assemble / integrate different components? You plan to have any sub-specific RC hardware on board such as a dedicated depth and pitch controller or a specific diving system (i.e. piston tank - diving bag - press. air system ) - how deep would like or trust yourself to go and whats your diving area - pool or pond? - freshwater or seawater? how important is it to have full telemetry access duri g your dives?

Thank you, @U-Boat. I’m using a Thunder Tiger Neptune hull. She has a static dive system. I put more details about the boat and the state of work in your Conventional Submarines thread.

I’m 300 miles from the closest seawater, although I found the hard way salt water pools are a problem :=) I’m starting out in fresh water pools while I’m tuning the controls. My goal is autonomous navigation on a (fresh water) lake. Telemetry is desirable but not yet onboard. I use 915 MHz in my fixed wing aircraft and have a unit available. I would like to avoid external hardware for control. I would prefer to add support for traditional submarines to ArduPilot. It already has most of the elements, just in different vehicle types. I want to control all aspects of the boat from one place, e.g., put depth in waypoint missions and MAVlink comamnds.

I added some screenshots of valid and invalid PPM traces to the first post in this thread.

@ iter if you agree we switch to the conventional sub thread because the title is more informative to the reader…

1 Like

Yes, thank you @U-Boat. That conversation belongs in your thread. Back to PPM noise. I’m trying to add unit tests for PPM, but I’m running into linking problems. I followed the instructions here: ArduPilot Unit Tests — Dev documentation and set up relevant tests/test_*.cpp and test/wscript files. I can run basic GTest test cases like EXPECT_EQ(1, 1) and they work correctly. I’m running into linker errors when I try to use actual AP classes, like this:

AP_RCProtocol frontend = AP_RCProtocol();

You can see the actual code here: Comparing master...ppm_sum_qc · arikrupnik/ardupilot · GitHub

Linker errors look like this:

/usr/bin/ld: lib/libap.a(AP_Frsky_MAVliteMsgHandler.cpp.0.o): warning: relocation against `hal' in read-only section `.text._ZN26AP_Frsky_MAVliteMsgHandler31handle_command_preflight_rebootERK24__mavlink_command_long_t'
/usr/bin/ld: lib/libap.a(AP_CRSF_Telem.cpp.1.o): in function `AP_CRSF_Telem::adjust_packet_weight(bool)':
/home/ari/ardupilot/build/linux/../../libraries/AP_RCTelemetry/AP_CRSF_Telem.cpp:370: undefined reference to `hal'
/usr/bin/ld: lib/libap.a(AP_CRSF_Telem.cpp.1.o): in function `AP_CRSF_Telem::calc_flight_mode()':
/home/ari/ardupilot/build/linux/../../libraries/AP_RCTelemetry/AP_CRSF_Telem.cpp:946: undefined reference to `hal'
/usr/bin/ld: /home/ari/ardupilot/build/linux/../../libraries/AP_RCTelemetry/AP_CRSF_Telem.cpp:951: undefined reference to `hal'
/usr/bin/ld: lib/libap.a(AP_CRSF_Telem.cpp.1.o): in function `AP_CRSF_Telem::calc_status_text()':
/home/ari/ardupilot/build/linux/../../libraries/AP_RCTelemetry/AP_CRSF_Telem.cpp:1413: undefined reference to `hal'
/usr/bin/ld: lib/libap.a(AP_CRSF_Telem.cpp.1.o): in function `AP_CRSF_Telem::get_singleton()':
/home/ari/ardupilot/build/linux/../../libraries/AP_RCTelemetry/AP_CRSF_Telem.cpp:1522: undefined reference to `hal'
/usr/bin/ld: lib/libap.a(AP_SerialManager.cpp.0.o):/home/ari/ardupilot/build/linux/../../libraries/AP_SerialManager/AP_SerialManager.cpp:608: more undefined references to `hal' follow
/usr/bin/ld: warning: creating DT_TEXTREL in a PIE
collect2: error: ld returned 1 exit status