jgromes / RadioLib

Universal wireless communication library for embedded devices
https://jgromes.github.io/RadioLib/
MIT License
1.48k stars 375 forks source link

Horus-Binary Compatible 4FSK Mode Support #306

Closed darksidelemm closed 2 years ago

darksidelemm commented 3 years ago

There are many amateur high-altitude balloon launching groups using this library to modulate radios on high-altitude balloon telemetry payloads. Most are using either APRS or RTTY.

Another modulation scheme which has gained a fair bit of traction, especially in Europe and Australia, is the 'Horus Binary' 4FSK mode, which uses 100 baud 4FSK modulation. The transmitted packets are encoded using Golay(23,12) encoding and end up being extremely robust, with ranges of >700km being reported. More information on this modulation, and the software to decode it, is here: https://github.com/projecthorus/horusdemodlib/wiki

Most days you can look on tracker.habhub.org and see a 4FSK-transmitting payload in the air.

The current 'reference platform' for this modulation is the Vaisala RS41 radiosonde, reprogrammed with the RS41HUP firmware, however it would be nice to expand what platforms can transmit it, and make it easier to integrate into other payloads. Not everyone has access to the 'STM32 dev board that falls from the sky' that is the Vaisala RS41...

While I would not want to burden this repository with all the packet encoding overhead, the ability to modulate the 4FSK tones is really all that is necessary. I've had a crack at this by using the RTTYClient as a base, in a branch here: https://github.com/darksidelemm/RadioLib/tree/fsk4 I've used the name FSK4Client to avoid numerics at the start of variable names, as per C conventions. The code is likely not quite compliant with your style guidelines (which I just noticed - sorry), but I can certainly clean it up.

I've also included example transmitter code here using a 'canned' encoded packet: https://github.com/darksidelemm/RadioLib/blob/fsk4/examples/FSK4/FSK4_Transmit/FSK4_Transmit.ino The transmission can be received using a SSB receiver (SDR or otherwise), and the software mentioned above.

So far I've tested this with a SX1278-based radio module, but do not have the hardware to test on other radio ICs. I haven't yet tried the AFSKClient side of things - at the moment it's commented out due to compilation issues relating to AFSKClient's _phy being private (Unsure why this doesn't work for me, but does work for RTTYClient...).

Anyway, I'm not sure if this is something you'd consider including - the 4FSK modulation is somewhat specific to this high-altitude balloon activity, but I'm also not sure how perform this modulation without adding a protocol into this Library. I guess I could just manually call the transmitDirect function externally, but it's certainly not as neat a solution.

Hence, I'm raising this issue to start the discussion! If you think it's too application-specific to include, then I'm happy to take another approach...

jgromes commented 3 years ago

I think this would make very good addition to RadioLib. Maybe instead of implementing a special case for 4-FSK (and duplicating code from RTTY in the process), it might be a better idea to have a generic MFSK implementation, similar to AFSK. The user (end-user of the library or some protocol/layer on top of it) could then specify the number of tones, their shifts and so on. Or maybe I'm overthinking it.

If it's working on SX1278, then it should work on all the other FSK modules supported by RTTY, provided the freqeuency shifts between tones are sufficient.

Regarding implementation of layers above 4-FSK, it depends on the complexity, applicability and reusability. If it's something that's widespread (even through just one specific community, such as HAB), then it's generally good addition. Especially if it can be reused in other protocols or projects (the question is essentially "will it be useful for somebody else other than myself?"). For example, having a Golay encoder/decoder would fit quite nicely with the usualy use case of this library. However, if it's a highly complex protocol, or would only be useful for one specific project, then it's probably better to build a layer on top of RadioLib, instead of integrating it directly.

Feel free to open a pull request and we can iterate the contribution from there. I'm very glad this library is finding its users among high-altitude ballonists :)

darksidelemm commented 3 years ago

I agree that a more generic MFSK implementation would be useful, but it definitely increases the complexity somewhat! Having to deal with N-order modulations (which gets more painful when you have things like 3-bits per symbol), different symbol mapping, then the inevitable 'why can't you do the 1.465 Hz tone spacing so I can do WSPR' :-)

I think it'll be better to keep the Golay encoding separate for now, especially since I may end up switching it out for something more modern in the future. I'll certainly be providing some example code (probably in another repo) showing how to integrate RadioLib and the horus_l2.c/h code (which does the Golay encoding, interleaving and scrambling) and develop a high-altitude balloon tracker.

I'll try and do some work on a PR over the next few weeks. What's the recommended test platform for producing AFSK? I have hit compilation issues when I include the overloaded constructors that take an AFSKClient object, as the _phy of the AFSKClient is private, so I need to figure that out then check AFSK works.

jgromes commented 3 years ago

I have hit compilation issues when I include the overloaded constructors

You'll have to add your FSK4Client class into AFSKClient friends, that will allow FSK4Client to access private methods of AFSKClient. It's a bit of a nasty hack, but I was too lazy to come up with a better solution. Regarding test platform, I usually use an SX127x or SX126x with Arduino Uno, but only because I have loads of these.

https://github.com/jgromes/RadioLib/blob/251dd438a028167895b3cda35f107da90d9fc1c7/src/protocols/AFSK/AFSK.h#L59-L64

OK, let's keep it simpler for now with just 4-FSK. If you have a specific project using RadioLib, feel free to let me know, as I keep a list of them on the Wiki :)

jgromes commented 3 years ago

@darksidelemm was there any progress on this? If not, I'll go ahead and implement it myself.

darksidelemm commented 3 years ago

Hi Jan,

Apologies for not progressing with this - just a lack of time so far on my part!

What's in my fsk4 branch is functional from the modulation perspective, with the resultant signal decodable using horus-gui (though the mask estimator needs to be disabled due to the tone spacing being slightly smaller than the default). I haven't tested it via AFSKClient, but i wouldn't recommend operating it that way (at least not through any radio chips) anyway, due to the extra double-modulation losses involved.

darksidelemm commented 3 years ago

On a related note - I should point out that in my 'write' function ( https://github.com/darksidelemm/RadioLib/blob/fsk4/src/protocols/FSK4/FSK4.cpp#L67 ) I am not going into standby between each transmitted byte of data. I go into standby at the end of the buffer.

I note that the RTTY code ( https://github.com/darksidelemm/RadioLib/blob/fsk4/src/protocols/RTTY/RTTY.cpp#L205 ) does goes into standby at the end of each byte, which I'm concerned may result in variations in overall symbol timing. This will resulting in degraded performance with FSK Demodulators that expect the transmission to have a consistent symbol timing.

You can see the effect of the standby() call here as short gaps between transmitted bytes:

Screen Shot 2021-09-08 at 9 40 33 pm

.. and zoomed in.

Screen Shot 2021-09-08 at 9 40 48 pm
jgromes commented 2 years ago

Thanks for pointing out the issue with call to standby() - to be perfectly honest, I'm not sure why I placed it there instead of the user code (where it makes a lot more sense). Fixed now.

darksidelemm commented 2 years ago

Also, I ended up doing an implementation of the 4FSK mode 'outside' of RadioLib here: https://github.com/projecthorus/horusbinary_radiolib

I'm happy to leave it at that, as it's a fairly application-specific mode.

jgromes commented 2 years ago

Like I said in one of the previous comments, I'm fine with adding 4FSK to RadioLib, it seems like a very "hackable" mode. So if you don't mind, I'd like to merge your fsk4 branch.

darksidelemm commented 2 years ago

Sure! As far as i'm aware its all functional (and I used it as the base for the separate implementation).

Once merged I'll re-do my example (which includes all the packet construction and encoding) to use the merged lib.

jgromes commented 2 years ago

The PR is merged, and I also added AFSK support, because why not ;)

Thanks for the contribution!

ManoDaSilva commented 2 years ago

Would there be a way to implement it on reception? It could be interesting for something like the TTGO modules (pinging @fred-corp too) :)

jgromes commented 2 years ago

@ManoDaSilva possibly, using the AFSK mode - recently I added a Morse reception, which works by exploiting the direct mode on SX1278 and similar modules. Basically, the module outputs a square wave of the same frequency as the Morse tone. So if you were to observe the frequency of the signal you could receive the data, but I don't know if it's a feasible approach in practice as well.

darksidelemm commented 2 years ago

I don't expect reception of this to be possible on a TTGO. We designed the modem to be received using SDR, for best performance...