Running rds_tx.grc/.py headless on ARM64 Orange Pi?
Sorry to bother you again :)
now, I installed gnuradio on my Orange Pi Zero 3 and tried to run the script headless by changing it to 'No GUI' in the options panel and exporting it to a .py-file. It certainly runs on the OPi and doesn't give any other errors than on my Linux Mint machine but there's no MPX generated. On my desktop machine the .py-script runs perfectly fine! Any help would be greatly appreciated!
System: OPi Zero 3, 2 Gb ram, Allwinner H618/Debian bookworm server, Linux 6.1.31-sun50iw9 kernel, ARM64; version 1.0.4 (Orange Pi release)
Edit: Maybe different python versions are a problem (3.10.12 on desktop vs. 3.11.2 on opi).
You seem to specify the audio devices manually (hw:4,1 and hw:3,0). Maybe the mapping is different on the different platforms, the devices support different sample rates, or you want to use a different audio subsystem (e.g. pulse)?
Thank you! Forgot to say that. I saved the installation logs of both gnuradio & deps as well as gr-rds, I can post them tomorrow. Nonetheless there were no errors when compiling & installing.
Also, I uninstalled & reinstalled gnuradio & deps after the first try but changed the repos to the official debian ones before reinstalling (they were set to a Huaweicloud from the Orange Pi devs). Maybe that helps.
I made sure that the mapping & sample rates are correct but I'll check tomorrow again. Maybe I'll add an entry to .asoundrc to make sure the USB-sound card is fixed to 192kHz. I deactivated pulse on the desktop beforehand already as I'm using jack (which only runs once started) - so when not using jack, only ALSA is running. On the debian server, I think there's also only ALSA installed but I'll make sure that this is the case tomorrow. Maybe it has pipewire or so, I'll kick that out.
I don't think that this is an installation issue. Since you mentioned that you ran the same script on your desktop, I just wondered if you forgot to adapt the device name parameter of the audio source and sink.
So, after uninstalling, changing deps to official ones and reinstalling, it finally works and even with the OPis internal sound card! But... CPU load is on 95% on the Orange Pi Zero 3, 2gb. RAM usage isn't a problem, less than 100mb. RDS pops up and goes off again. So the OPi is underpowered. Luckily, I still have a couple of HP 260 G2 DMs with Pentium 4405U CPUs laying aroung which will be perfect for this job (I hope). I'll install a headless Debian on them as well. Now I just need to get the dynamic PS (changing every 3 secs) to work :D
I just tested it and it really takes much more CPU than I would have expected. That's in no way optimized :-/ I hope, it works with your other device.
A bunch of blocks have Maxoutbuf set to 10, which forces samples to be processed in very small batches. This seems to increase CPU load a lot.
Unfortunately, removing that results in a lot more output buffer underruns. The Audio Source block has "OK to Block" set to "No" to avoid the two-clock problem, but unfortunately that setting is ignored in nearly all audio backends (except Portaudio, which is unlikely to be used because ALSA has a higher priority).
I understand. So setting a fixed, higher, buffer size and inputting audio with a fixed (e.g. 4096), higher, buffer size (-bufsize?) using ffmpeg is not a solution?
Edit: If I'm not mistaken, buffer under- over overruns can lead to pops and crackling sounds but maybe it would be worth a try to see if that helps and how bad the effects are. I think StereoTool as an MPX generator also uses a standard buffer size of 4096.
You could try changing the outbuffer variable from 10 to 0, and see whether that helps.
Also, for your use case (FM modulation done in hardware) there's no reason to use an internal sample rate of 380 ksps. You could use 192 ksps instead, but that would require a bunch of changes to the flow graph. (Change usrp_rate to 192000, change audio Rational Resamplers to interpolation 4 & decimation 1, change Root Raised Cosine Filter to interpolation 16, gain 11.1, sample rate 38000, num taps 16*11, add a Rational Resampler block after Root Raised Cosine Filter with interpolation 192 & decimation 38, and remove the Rational Resampler block on the output.)
I expect those changes would reduce CPU utilization quite a bit.
Awesome :D Actually tried that already but changed only the sample rate which didn't work out.
Edit: Yep, those steps reduced CPU load quite drastically to about max. 7%. It always jumps between 1 and 7. Now I just need to connect it to my tx which I sadly can't do in this exact moment but you'll hear from me later today.
One small correction (which I edited in above): Root Raised Cosine Filter interpolation should be 16.
Boomshakalaka! It works like a charm! CPU load is at about 5-8% when playing & resampling audio to 48kHz in FFMPEG and encoding MPX (stereo & RDS) at the same time. I'm gonna finish my tutorial and post it today, crediting you of course. I'll post a link here as well. Also, I added both of my working project files, the .grc & the .py. Thank you very much again! rds_tx_for_opi_1.0.zip
One last thing: from the screenshot above, it looks like you weren't using the latest version of the rds_tx.grc flow graph. You'll want to incorporate the recent changes made in #79, otherwise FM stereo channel is not going to work properly with the 192 ksps intermediate sample rate. (The left & right channels may not remain separated.)
Here's that one with latest version of the rds_tx.grc flow graph. rds_tx_for_opi_1.0.zip
Edit: Here's my tutorial on Radionecks: https://radionecks.co.uk/viewtopic.php?t=3690
Nice to see you got it working!
It should be possible to do dynamic RDS updates via TCP if you put back the Socket PDU block and connect it to the RDS Encoder. Some details in this issue: #44
Latency is probably going to be an issue, since a lot of RDS bits will probably build up in GNU Radio buffers. gr-rds should probably have some latency management in the style of gr-latency_manager. References:
- https://www.youtube.com/watch?v=jq0RewceCwc
- https://github.com/dkozel/gr-latency_manager
#84 should fix the CPU utilization problem without having to disable the output buffer limits, and it also reduces RDS latency substantially.
Did I understand it right that you basically proposed two (actually 3) things: Removing the Maxoutbuf setting for these blocks (Multiply, Add, Frequency Mod)
Also, two low-sample-rate blocks (RDS Encoder, Chunks to Symbols) do not specify a maximum output buffer size. Adding the setting to the RDS Encoder block
I've added the setting to the Chunks to Symbols block as well, but unfortunately it has no effect at the moment due to a bug
Then I'll give it a try later using the latest flowgraph (or are those specific changes already included here?).
The changes I proposed in #84 are:
- Multiply, Add, Frequency Mod: change Maxoutbuf from
outbufferto 0 - RDS Encoder, Chunks to Symbols: change Maxoutbuf from 0 to
outbuffer
If you make those changes in your flow graph (apart from the Frequency Mod block, which your flow graph doesn't have) you should be able to set the outbuffer variable back to 10 to keep RDS latency under control.
There will still be 20 seconds of RDS latency, which is not ideal, but it's much better than three minutes, which you'll get with the current flow graph if you set outbuffer to 0.
Sorry for my late reply, here's the updated version: rds_tx_for_opi_1.1.zip
Looks good.
By the way, you could shave another 7 seconds or so off the RDS latency by manually editing the .py file to replace:
self.digital_chunks_to_symbols_xx_0.set_max_output_buffer(10)
with
self.digital_chunks_to_symbols_xx_0.set_max_output_buffer(0, 10)
This is a workaround for this bug: https://github.com/gnuradio/gnuradio/issues/7534
I got good news. After some time, I managed to get the dynamic RDS going. Here's my python code for the socket PDU client:
import socket import time
HOST = 'localhost' # oder die IP-Adresse des Servers PORT = 52001 # Port des Socket PDU Blocks in GNU Radio
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((HOST, PORT))
try: while True: s.send(b"ps Radio\n") time.sleep(3) s.send(b"ps Berlin\n") time.sleep(3) except KeyboardInterrupt: s.close()
I have one last question before we can actually close this issue.
Could you please explain to me, how I can generate a stereo-signal that consists of the normal L/R-stereo-channels AND only the RDS-signal on top of that (no MPX/preemphasis).
This would be very useful to inject RDS into those small chinese transmitters that use FM-ICs. I'm succesfully doing that already using for example Airomate or StereoTool - you can do the mixing of L/R & RDS digitally or analogue, both works. It would be cool to have it one application though.
I tried it already once by reducing the flowgraph/eliminating the unnecessary nodes but it didn't work out for me. I assume that I need to change the Multiply & Add additionally - also asked ChatGPT for help but couldn't figure it out.
Many thanks for your help! gabba43
This would be very useful to inject RDS into those small chinese transmitters that use FM-ICs.
Could you show us what type of transmitter you're talking about? What does it expect to receive as input?
It expects a normal non-multiplexed stereo-signal as the input. The chip does the low-pass-filtering, preemphasis & multiplexing.
As a matter of fact though, you can actually transmit RDS by mixing the stereo-signal with the RDS-signal (can be on one or both channels). RDS-subcarrier won't be synced to the pilot but it still works (2007 Mazda car radio & Tef6686).
I've tried it myself with this particular tx using the BH1418 as well as with another one using the BH1415F and a third one using the QN8007. The BH chips work best using this method.
Have a look at this, it was first discovered by Pira.cz in 2012 or by the guys from DoItYourselfChristmas:
https://pira.cz/rds/rdsmp3tx.pdf https://doityourselfchristmas.com/forum/index.php?threads/anybody-added-an-rds-to-a-chinese-fm-transmitter-yet.33910/ https://radionecks.co.uk/viewtopic.php?t=2508
It could certainly be done, but many adjustments would have to be made to the example rds_tx.grc flow graph, and you'd have to use a sound card with support for 192 kHz output.
I'm not inclined to spend time on it though, since the resulting RDS signal quality seems to be low, and the gr-rds project is intended for use with SDR transmitters that can produce much cleaner output.
Thank you for your reply! I have multiple 192/384 kHz soundcards laying around. Maybe I'll give it a try in the future.
Btw, so far, using gr-rds as an MPX encoder on the Orange Pi 3 works like a charm.
For now, my plan is to record Airomate's RDS output to a .wav-file and mix it together with the webstream on my Linux Mint machine using a simple dmix device.
Did you check your SDR's (Hackrf?) spectral purity on a spectrum analyzer? My chinese 120w FM-transmitter based off the BH1418 doesn't interfere with any other FM station - even weaker ones. I was positively surprised about it's spectral purity on the spectrum analyzer. AFAIK it's certainly a lot cleaner than those SDRs.
I wasn't thinking of the overall spectral purity (external filtering would be needed to remove harmonics from most general-purpose SDRs) but rather the stated limitations of the MP3 transmitters. From the pica.cz document:
Internal limiter and stereo encoder causes considerable reduction of RDS signal quality injected that way. Pilot and RDS synchronization is omitted. Due to poor quality of the integrated stereo encoder there's a strong 3rd pilot harmonic at 57 kHz, interfering with the RDS signal.
An SDR transmitter would not have these issues.
I found in this thread useful code to control RDS Encoder by sending data to TCP socket, send by @gabba43. Based on this code I wrote (with help of ChatGPT) an Embedded Python Block that can be used to change RDS data from GRC. I think someone might find this code useful, so I decided to share it in this thread:
Latency is probably going to be an issue, since a lot of RDS bits will probably build up in GNU Radio buffers. gr-rds should probably have some latency management in the style of gr-latency_manager.
I have now implemented latency management in #104, eliminating the need to use the Maxoutbuf setting as a workaround. I'd be interested to get feedback on that change.