Longmynd on a Jetson Nano

Digital ATV - The latest generation, cutting edge ATV - Please discuss it all here.
Forum rules
This forum is run by the BATC (British Amateur Television Club), it is service made freely available to all interested parties, please do not abuse this privilege.

Thank you
G7JTT
Posts: 338
Joined: Fri Jun 10, 2016 5:05 pm

Longmynd on a Jetson Nano

Post by G7JTT » Sat May 16, 2020 7:50 pm

Hi all thought I'd share my setup on the Nano using the Minitiouner, Longmynd software and controlling it with Robs (M0DTS) Multi Quick Tune . Thanks too Rob M0DTS for the original Python script and ideas which all this is based on.

Firstly install Longmynd from the instruction on the GitHub site https://github.com/myorangedragon/longmynd

Then create the file longmynda.sh with the following text in the home directory of the Nano. Change the udpsink host address to your local address if you want to upd stream to something else i.e OBS. Then from a terminal window type chmod +x ./longmynda.sh to make it executable.

Code: Select all

#!/bin/bash
clear
resize -s 12 26
clear

cd ~/longmynd # Longmynd install directory
./lm_display.py &


while true; do
clear 


echo "listening on port 6789"
netcat -ul 6789 | while read line #send commands via Multi Quick Tune on port 6789
do
  echo "$line"
  freq=$( awk -F',' '{print $2}'  <<< $line | awk -F'=' '{print $2}' )
  sr=$( awk -F',' '{print $5}'  <<< $line | awk -F'=' '{print $2}' )
  lnb_offset=$( awk -F',' '{print $3}'  <<< $line | awk -F'=' '{print $2}' )
  echo "$freq"
  echo "$sr"
  echo "$lnb_offset"
  
freq2=$((freq - lnb_offset))
echo "Rx $freq with a symbolrate of $sr"
# Start Rx process 
killall totem
totem udp://230.0.0.10:1234 & # Start player
killall gst-launch-1.0
killall gst-launch-1.0
gst-launch-1.0 udpsrc port=1234 ! tee name='repeat' ! queue ! udpsink host=192.168.1.101 port=5000 & # change address to set up udp stream 
killall longmynd 
./longmynd -i 230.0.0.10 1234 $freq2 $sr & # restart longmynd with params 

done &

while read -sn 1 quit && [[ $quit != " " ]]; # Press the space bar at any time to quit
do
xdotool getactivewindow windowkill
done
done

Now create lm_display.py in the longmynd directory with the following text

Code: Select all

#!/usr/bin/env python
# Original script By Rob M0DTS modified by John G7JTT
# Copy this script to the installed directory of Longmynd
import time
import os
import signal
import json
import struct
import fcntl






lm_states =['Initialising','Searching','Got Headers','Locked DVB-S','Locked DVB-S2']
lm_DVBS_modcodes =['1/2','2/3','3/4','5/6','6/7','7/8']
lm_DVBS2_modcodes =['Dummy','QPSK 1/4','QPSK 1/3','QPSK 2/5','QPSK 1/2','QPSK 3/5','QPSK 2/3','QPSK 3/4','QPSK 4/5','QPSK 5/6','QPSK 8/9','QPSK 9/10','8PSK 3/5','8PSK 2/3','8PSK 3/4','8PSK 5/6','8PSK 8/9','8PSK 9/10','16APSK 2/3','16APSK 3/4','16APSK 4/5','16APSK 5/6','16APSK 8/9','16APSK 9/10','32APSK 3/4','32APSK 4/5','32APSK 5/6','32APSK 8/9','32APSK 9/10']
lm_Dnum_DVBS =['2.7','4.4','5.5','6.5','7.2']
lm_Dnum_DVBS2=['Dummy','-2.4','-1.2','0.0','1.0','2.2','3.1','4.0','4.6','5.2','6.2','6.5','5.5','6.6','7.9','9.4','10.6','11','9','10.2','11','11.6','12.9','13.1','12.6','13.6','14.3','15.7','16.1']



def handler(signum, frame):
	
	time.sleep(0.5)
	print '\nClosing...\n'
	exit()

signal.signal(signal.SIGINT, handler)	#catch ctrl-c better!







def read_lm_status(fifo):
	global threadLock
	global lm_final_status
	global newstatus
	global lm_status

	readpid = False

	lm_status  = {'Rx': '0','Freq': '','LO':'', 'SR': '', 'MODCODE': '', 'Call': '', 'Provider': '', 'State': '', 'Viterbi': '', 'BER': '', 'MER': '', 'Nulls': '', 'ShortFrames': '', 'Pilots': '','APID':'','ATYPE':'','VPID':'','VTYPE':''}
	f = open(fifo, "r")

	VT="No Video"
	AT="No Audio"
	Dn=0
	a=0
	b=0

	while True:
		try:
			line = f.readline().strip("\n").split(",")
			#print line
		except:
			print "Can't open fifo"
			exit()


		if line[0]=="$1":
			lm_status["State"]=lm_states[int(line[1])]
		if line[0]=="$6":
			lm_status["Freq"]=line[1]
		if line[0]=="$9":
			lm_status["SR"]=line[1]
		if line[0]=="$10":
			lm_status["Viterbi"]=line[1]
		if line[0]=="$11":
			lm_status["BER"]=line[1]
		if line[0]=="$12":
			#print int(line[1])
			if int(line[1])>512:
				lm_status["MER"]=-(float(1024-int(line[1])))/10
			else:
				lm_status["MER"]=float(line[1])/10
		if line[0]=="$13":
			lm_status["Call"]=line[1]
		if line[0]=="$14":
			lm_status["Provider"]=line[1]
		if line[0]=="$15":
			lm_status["Nulls"]=line[1]
		if line[0]=="$16":
			if readpid:
				lm_status["APID"]=line[1]
			else:
				lm_status["VPID"]=line[1]
		if line[0]=="$17":
			if readpid:
				lm_status["ATYPE"]=line[1]
				readpid=False
			else:
				lm_status["VTYPE"]=line[1]
				readpid=True
		if line[0]=="$18":
			if lm_status['State']=="Locked DVB-S":
				lm_status["MODCODE"]=lm_DVBS_modcodes[int(line[1])]
			if lm_status['State']=="Locked DVB-S2":
				try:
					lm_status["MODCODE"]=lm_DVBS2_modcodes[int(line[1])]
				except:
					lm_status["MODCODE"]="invalid"
		if line[0]=="$18":
			if lm_status['State']=="Locked DVB-S":
				b=float(lm_Dnum_DVBS[int(line[1])])
			if lm_status['State']=="Locked DVB-S2":
				try:
					b=float(lm_Dnum_DVBS2[int(line[1])])
				except:
					b=0
		if line[0]=="$19":
			if lm_status['State']=="Locked DVB-S2":
				lm_status['ShortFrames']=line[1]
		if lm_status["VTYPE"]=="36":
			VT="H265"
		if lm_status["VTYPE"]=="27":
			VT="H264"
		if lm_status["VTYPE"]=="2":
			VT="MPEG-2"
		if lm_status["ATYPE"]=="3":
			AT="MPGA"
		if lm_status["ATYPE"]=="15":
			AT="AAC"
		if lm_status["ATYPE"]=="129":
			AT="AC-3"
		if line[0]=="$20":
			if lm_status['State']=="Locked DVB-S2":
				lm_status['Pilots']=line[1]
			a=lm_status["MER"]
			Dn = a - b
			#last line of output

			###########################################################################################
			#add lines here to display in cosole
			print "\nCall: "+str(lm_status["Call"])
			print "Provider: "+str(lm_status["Provider"])
			print "Mod: "+str(lm_status["MODCODE"])
			print "MER: "+str(lm_status["MER"])+"db"
			print "D:",Dn		
			print "Freq: "+str(lm_status["Freq"])+"KHz"
			print "SR: "+str(lm_status["SR"])
			print "Nulls: "+str(lm_status["Nulls"])+"%"
			print "Aud: "+(AT)
			print "Vid: "+(VT)
			print "Status: "+str(lm_status["State"])
			###########################################################################################

		time.sleep(0.001)
	#	print lm_status
	f.close()


#run the loop reading status fifo and display the nice version.

read_lm_status("/home/g7jtt-nano/longmynd/longmynd_main_status") #this is the path to the status fifo file

You should now be able to open up a terminal and type ./longmynda.sh to run it, to stop it click on the info window and hit the space bar to quit. To control longmynd you need to set up Multi Quick Tune with the correct IP address and the port for your setup, also the correct LNB LO. This may even work on other linux machines in which case you may want to swap out Totem Player for VLC.

Longmynd_nano.jpg
Longmynd_nano.jpg (472.55 KiB) Viewed 8634 times

Hopefully some may find this useful and remember I'm no expert at any of this just like tinkering ! Once again thanks to Rob for the original script.

73 John G7JTT

G7JTT
Posts: 338
Joined: Fri Jun 10, 2016 5:05 pm

Re: Longmynd on a Jetson Nano

Post by G7JTT » Mon May 18, 2020 4:47 pm

Ok a slight update if you replace the line below in longmynda.sh

Code: Select all

netcat -ul 6789 | while read line
with this,

Code: Select all

netcat -k -ul -p 6789 | while read line
You will be able to send UDP commands from anywhere on the same network including phones and tablets with out having to restart longmynda.sh. Before the code would only let one connection, where as now the connection is closed before restarting. Now you can create a script called man.sh which will let you manually control longmynd over UDP. As I use this mainly on QO100 Ive hard coded the LO frequency in with an option at the beginning to use it or not (one less number to type in). I find this useful when two stations are close and Multi Quick Tune see them as one station. If you want to use this on your phone or tablet then just use a UDP sender app and manually send the line below with appropriate numbers(Freq,Srate and Offset) to the nano's IP address and port 6789. Hopefully the app will let you save it and then you only need to edit Freq and Srate when you want to send the new parameters.

[GlobalMsg],Freq=10491500,Offset=9749854,Doppler=0,Srate=1500,WideScan=0,LowSR=0,DVBmode=Auto,FPlug=A,Voltage=0,22kHz=Off

Code: Select all

#!/bin/bash
clear
echo "Using an LNB ? y/n"
read lnb 
if [[ $lnb = "y" ]] 
then offset=9749854 # This is your LNB LO frequecy
else offset=0
fi
nc localhost 6789
while true; do
clear
echo "Input Frequency in KHz"
read freq
clear
echo "Input SR"
read sr
clear
# echo "[GlobalMsg],Freq=${freq},Offset=${offset},Doppler=0,Srate=${sr},WideScan=0,LowSR=0,DVBmode=Auto,FPlug=A,Voltage=0,22kHz=Off" | nc -u -w1 230.0.0.12 6789
echo "[GlobalMsg],Freq=${freq},Offset=${offset},Doppler=0,Srate=${sr},WideScan=0,LowSR=0,DVBmode=Auto,FPlug=A,Voltage=0,22kHz=Off"  >/dev/udp/localhost/6789

done

Still twiddling John G7JTT

G7JTT
Posts: 338
Joined: Fri Jun 10, 2016 5:05 pm

Re: Longmynd on a Jetson Nano

Post by G7JTT » Sun May 24, 2020 5:17 am

Some more twiddling has been done firstly update the lm_display.py with the code below

Code: Select all

#!/usr/bin/env python
import time
import os
import signal
import json
import struct
import fcntl






lm_states =['Initialising','Searching','Got Headers','Locked DVB-S','Locked DVB-S2']
lm_DVBS_modcodes =['1/2','2/3','3/4','5/6','6/7','7/8']
lm_DVBS2_modcodes =['Dummy','QPSK 1/4','QPSK 1/3','QPSK 2/5','QPSK 1/2','QPSK 3/5','QPSK 2/3','QPSK 3/4','QPSK 4/5','QPSK 5/6','QPSK 8/9','QPSK 9/10','8PSK 3/5','8PSK 2/3','8PSK 3/4','8PSK 5/6','8PSK 8/9','8PSK 9/10','16APSK 2/3','16APSK 3/4','16APSK 4/5','16APSK 5/6','16APSK 8/9','16APSK 9/10','32APSK 3/4','32APSK 4/5','32APSK 5/6','32APSK 8/9','32APSK 9/10']
lm_Dnum_DVBS =['2.7','4.4','5.5','6.5','7.2']
lm_Dnum_DVBS2=['Dummy','-2.4','-1.2','0.0','1.0','2.2','3.1','4.0','4.6','5.2','6.2','6.5','5.5','6.6','7.9','9.4','10.6','11','9','10.2','11','11.6','12.9','13.1','12.6','13.6','14.3','15.7','16.1']



def handler(signum, frame):
	
	time.sleep(0.5)
	print '\nClosing...\n'
	exit()

signal.signal(signal.SIGINT, handler)	#catch ctrl-c better!







def read_lm_status(fifo):
	global threadLock
	global lm_final_status
	global newstatus
	global lm_status

	readpid = False

	lm_status  = {'Rx': '0','Freq': '','LO':'', 'SR': '', 'MODCODE': '', 'Call': '', 'Provider': '', 'State': '', 'Viterbi': '', 'BER': '', 'MER': '', 'Nulls': '', 'ShortFrames': '', 'Pilots': '','APID':'','ATYPE':'','VPID':'','VTYPE':''}
	f = open(fifo, "r")

	VT="No Video"
	AT="No Audio"
	Dn=0
	a=0
	b=0
	LO=9749854
	H=10500000
	L=10492000
	O=0
	Q=0
	F=0
	C=0
	Z=0
	N=0
	while True:
		try:
			line = f.readline().strip("\n").split(",")
			#print line
		except:
			print "Can't open fifo"
			exit()
			

		if line[0]=="$1":
			lm_status["State"]=lm_states[int(line[1])]
		if line[0]=="$6":
			lm_status["Freq"]=line[1]
			Q = float(lm_status["Freq"])
			F = Q / 1000
			C = format((Q / 1000),'.3f')			
			O = format(((Q + LO) / 1000),'.3f')
		if 739 < F < 751 :
			V=str(O)				
			Z=" MHZ"
			
		else:
			V=""
			Z="N/A"
		if line[0]=="$9":
			lm_status["SR"]=line[1]
			M=float(lm_status["SR"])
			N = int(round ((M /1000) + 0.85))
		if N == 332:
				N = 333
		if N == 124:
				N = 125
		if line[0]=="$10":
			lm_status["Viterbi"]=line[1]
		if line[0]=="$11":
			lm_status["BER"]=line[1]
		if line[0]=="$12":
			#print int(line[1])
			if int(line[1])>512:
				lm_status["MER"]=-(float(1024-int(line[1])))/10
			else:
				lm_status["MER"]=float(line[1])/10
			
		if line[0]=="$13":
			lm_status["Call"]=line[1]
		if line[0]=="$14":
			lm_status["Provider"]=line[1]
		if line[0]=="$15":
			lm_status["Nulls"]=line[1]
		if line[0]=="$16":
			if readpid:
				lm_status["APID"]=line[1]
			else:
				lm_status["VPID"]=line[1]
		if line[0]=="$17":
			if readpid:
				lm_status["ATYPE"]=line[1]
				readpid=False
			else:
				lm_status["VTYPE"]=line[1]
				readpid=True
		if line[0]=="$18":
			if lm_status['State']=="Locked DVB-S":
				lm_status["MODCODE"]=lm_DVBS_modcodes[int(line[1])]
			if lm_status['State']=="Locked DVB-S2":
				try:
					lm_status["MODCODE"]=lm_DVBS2_modcodes[int(line[1])]
				except:
					lm_status["MODCODE"]="invalid"
		if line[0]=="$18":
			if lm_status['State']=="Locked DVB-S":
				b=float(lm_Dnum_DVBS[int(line[1])])
			if lm_status['State']=="Locked DVB-S2":
				try:
					b=float(lm_Dnum_DVBS2[int(line[1])])
				except:
					b=0
		if line[0]=="$19":
			if lm_status['State']=="Locked DVB-S2":
				lm_status['ShortFrames']=line[1]
		if lm_status["VTYPE"]=="36":
			VT="H265"
		if lm_status["VTYPE"]=="27":
			VT="H264"
		if lm_status["VTYPE"]=="2":
			VT="MPEG-2"
		if lm_status["ATYPE"]=="3":
			AT="MPGA"
		if lm_status["ATYPE"]=="15":
			AT="AAC"
		if lm_status["ATYPE"]=="129":
			AT="AC-3"
		if line[0]=="$20":
			if lm_status['State']=="Locked DVB-S2":
				lm_status['Pilots']=line[1]
			a = lm_status["MER"]
			Dn = a - b
			

		
			#last line of output

			###########################################################################################
			#add lines here to display in cosole
			print "\nCall: "+str(lm_status["Call"])
			print "Provider: "+str(lm_status["Provider"])
			print "QO100: "+(V)+(Z)
			print "Freq: ",C,"MHz"
			print "Mod: "+str(lm_status["MODCODE"])
			print "SR:",N			
			# print "SR: "+str(lm_status["SR"])
			print "MER: "+str(lm_status["MER"])+" db"
			print "Req-MER:",b,"db"			
			print "D:",Dn		
			print "Nulls: "+str(lm_status["Nulls"])+"%"
			print "Aud: "+(AT)
			print "Vid: "+(VT)
			# print N
			print "Status: "+str(lm_status["State"])
			###########################################################################################

		time.sleep(0.001)
	#	print lm_status
	f.close()


#run the loop reading status fifo and display the nice version.
#this is the path to the status fifo file
read_lm_status("/home/g7jtt-nano/longmynd/longmynd_main_status")

This now adds a few extra lines of info to the display and don't forget to change at the end to you install directoy.

Now install guake with

Code: Select all

sudo apt install guake
this will allow us to use the drop down menu to display the info window.

Run longmynd by typing the following

Code: Select all

guake -n "Longmynd" -r "1" -e ./longmynda.sh
At this point press F12 and the "Listening on port 6789" will drop down, press again and it disappears. Find a signal (at this point totem it's not fullscreen) when you have have a signal press F12 to reveal the info window. To edit text colour and size right click on the info window and click on preferences. The appearance tab you can set the font size and colour, I use FreeMono Bold 40 and the homebrew schemes and set transparency to the far left. Now go to main window tab and adjust the Height and Width so all the text is displayed, that's it for now so close the preference window and stop longmynd with the space bar.

Now edit longmynda.sh change this line

Code: Select all

totem udp://230.0.0.10:1234 & # Start player
to

Code: Select all

totem --fullscreen udp://230.0.0.10:1234 & # Start player
this will run Totem in fullscreen.

So now you should be able to open a terminal and enter

Code: Select all

guake -n "Longmynd" -r "1" -e ./longmynda.sh
Click on a signal in multi quick tune and when locked the video should be fullscreen, to see the info window just press F12 and to hide it press F12 again.

Have fun John

G7JTT
Posts: 338
Joined: Fri Jun 10, 2016 5:05 pm

Re: Longmynd on a Jetson Nano

Post by G7JTT » Sun May 24, 2020 7:49 am

Oh and one other thing, I set my side bar to auto hide as well so when I select F12 only the info windows is displayed and not the side bar as well.

John

KA5BBC
Posts: 161
Joined: Thu Jun 28, 2018 6:59 pm

Re: Longmynd on a Jetson Nano

Post by KA5BBC » Sun May 24, 2020 5:31 pm

I am going to attempt to replicate this on a TX2 development board, hopefully this week, alongside some other ideas.

Hopefully the dual CPUs, 256 Pascal cores, and 4Kp60 encode/decode will help. ;-)
Andy, KA5BBC/MM0BQV

G7JTT
Posts: 338
Joined: Fri Jun 10, 2016 5:05 pm

Re: Longmynd on a Jetson Nano

Post by G7JTT » Sun May 24, 2020 6:46 pm

Works fine on the Nano, even when using it to encode H265 at the same time too. Only issue is no audio from any one who uses express software. Can't work it out as others that use mpga audio decode fine? I'm guessing a better video play might work but so far only totem supports the nano HW encoder/decoder. Good luck getting it going on the TX2 hope it works 👍

73 John

KA5BBC
Posts: 161
Joined: Thu Jun 28, 2018 6:59 pm

Re: Longmynd on a Jetson Nano

Post by KA5BBC » Mon May 25, 2020 10:21 am

I have seen that OBS can be installed directly onto a Nano and drive a Pluto to transmit a video signal. It would be interesting to see if the more heavy duty boards like the TX2 or the Xavier can run OBS to act as a source for Portsdown, or possibly while running the Portsdown software at the same time...
Way beyond my scope health knowledge and experience but an interesting thought experiment nonetheless - I don't envy anyone the task trying to port it all.
Andy, KA5BBC/MM0BQV

G7JTT
Posts: 338
Joined: Fri Jun 10, 2016 5:05 pm

Re: Longmynd on a Jetson Nano

Post by G7JTT » Mon May 25, 2020 10:28 am

As far as I'm aware OBS won't run on the nano, other wise I'd have it running on there and scrap using the laptop all together.

John

G7JTT
Posts: 338
Joined: Fri Jun 10, 2016 5:05 pm

Re: Longmynd on a Jetson Nano

Post by G7JTT » Tue May 26, 2020 5:14 am

I stand corrected Jacinto CU2ED sent me these links to running OBS on a Jetson Nano, I haven't tried it yet will try later in the week when my internet is restored.

https://qiita.com/kitazaki/items/3950c48a68bab1766fb6

https://github.com/kitazaki/jetson_nano_obs-studio

73 John

KA5BBC
Posts: 161
Joined: Thu Jun 28, 2018 6:59 pm

Re: Longmynd on a Jetson Nano

Post by KA5BBC » Wed May 27, 2020 4:53 pm

I look forward to reading your post..
Andy, KA5BBC/MM0BQV

Post Reply

Return to “DATV - Digital ATV”