DVB-S zender met 1x Raspberry PI3 en RPIDATV

Uit Projectgroep .540
Ga naar: navigatie, zoeken

Pi3-dvb-screen.png Pi3-dvb-screen2.png


Schermafbeelding van Python Script VCO lock op 1260 MHz en 2e Schermafbeelding van Python script op 436 Mhz en Multicast stream aktief



Beschrijving :

DVB-S zender op basis van Raspberry PI-3, DVB-S opzet printje van PA0RJV, VCO 35 Mhz-4400 Mhz, software van F5OEO, RPIDATV en besturing van PE2JKO. Enige rvaring met Linux en Raspberry PI is aanbevolen,

Bronnen :

Raspberry PI-3 - https://www.sossolutions.nl/raspberry-pi-model-3b?gclid=COjLtLXNis4CFcKVGwodEoAK0g

DVB-S opzet printje PA0RJV - http://www.pg540.org/wiki/index.php/SETUP-DATV_DVB-S_Print_for_Raspberry_PI_in_the_English_language

VCO 35-4400 Mhz ADF4351-development-board op AliExpress ALI

RPIDATV - https://github.com/F5OEO/rpidatv

In een vorige bouw beschrijving van een DATV zender heb ik gebruik gemaakt van 2 Raspberry PI computers, een RPI die als modulator dient (met UglyDATV) en een RPI-2 die zorg draagt voor het maken van de streams gebruik makend van multicast. Dit ontwerp is gebaseerd op 1 Raspberry PI-3, die verzorgt de stream en ook de DVB-S modulator en de besturing. Ik maak gebruik van basis elementen welke terug te vinden zijn als bron vermeldingen. Eigen ontwerp is de besturing van VCO en aansturing van de streams. Zal dus op dit gebied proberen te beschrijven wat er gebeurd. Informatie inzake bronvermeldingen is er volop op internet te vinden, daar ga ik hier niet dieper op in.

Motivatie:

De VCO is een belangrijk onderdeel van een DVB-S zender en frequentie instelling dient eenvoudig te zijn en waar je op kunt vertrouwen, er zijn al voldoende variabelen die succes van DVB-S kunnen beinvloeden, dus VCO moet gewoon werken. Ik wilde dit door de Raspberry PI-3 besturen. Niet gehinderd door enige kennis heb ik het als volgt opgelost, ik heb ervoor gezorgd dat de ingebouwde bleutooth UART van de Raspberry PI-3 wordt uitgeschakeld waardoor UART naar de GPIO pennen kunnen worden gebruikt. Op GPIOpin 14 en 15 kan ik daardoor dmv een programma geschreven in C commandoś sturen naar een Atmega-8 die ik als opsteekprintje op de Raspberry PI-3 heb gemonteerd. Het programma stuurt inhoudt van een file naar die Atmega-8, de ATmega8 ontvangt deze frequentie instellingen en zet de registers van de ADF4351. De VCO vertelt of ie gelocked is en schrijft een file terug op de Raspberry PI-3. Een Python script zorgt nu om het geheel met elkaar samen te laten werken. Het python script is gebouwd om dmv een muis of touch screen in te stellen wat de frequentie moet zijn en welke stream er moet worden opgestart. De besturing is goed leesbaar en daardoor ook aanpasbaar naar je eigen wensen. Een VCO lock of (unlock) wordt grafisch weergegeven door een groene of rode box van de frequentie weergave. Als deze groen is dan is dat een indicatie dat de VCO gelocked is, als het rood is dan is de frequrentie niet in lock en zal DVB-S niet goed worden ontvangen. Op dit moment kun je kiezen uit 7 verschillende streams en 4 verschillende frequenties. Dat is engiszins gelimiteerd, maar voldoende op dit moment voor mijn toepassing, uitbreiding is onbeperkt mogelijk vanwege gebruik van Python.

Wat is belangrijk, het bouwen van het opzet printje voor de Rasperry PI waarop een atmega8 zit, die zit met 5 draden aan de VCO printje van AliExpress, de ADF4351. LE/Data/Clock/MUXOUT/5v/GND.
Screenshot from 2016-07-25 22-42-45.png
Het programma serpush.c leest uit een file (~/freq) de frequentie, die stuurt ie via RS232 naar ATmega8 en die stuurt weer de 5 registers aan afhankelijk van de gewenste frequentie. De source van de ATMEGA8 is ook beschikbaar. Die is door dmv Bascom (Basic Compiler) gemaakt (E-MAIL naar pe2jko@pg540.org). Als de frequentie is ontvangen worden de registers gezet en zal de ADF4351 melden of ie gelocked is, dat stuurt het opzet boardje terug en wordt door de raspberry PI-3 herkent/opgeslagen in de file ~/lock.

Gebruikte controle files :

freq bepaalt frequentie van VCO (bv 1252.50)

lock bepaalt of VCO gelocked is ( 1 = locked, 0 = unlocked)

rate status geselecteerde stream


Het opzet printje heb ik zodanig gemaakt dat de 5v van de pennen van de GPIO komt, omdat ik een ATMEGA8 gebruik moet ik er wel voor zorgen dat de verbinding naar VCO board dmv een spanningsdeler van 5v naar 3.3 volt gaat want het VCO boardje wil graag 3.3v signalen zien. Ik verbind de de TX van GPIO pin naar de RX van de ATMEGA8 en de RX van de rapsberry PI3 naar de TX van de ATMEGA8. Het opzet printje verzorgt ook de spanning naar het VCO boardje. De ATMEGA8 werkt met een kristal (niet zo kritisch, best = 7,3728 MHz) en heeft een connector om inline te programmeren (ISP). Zodra printje aan gaat zal er worden geluisterd op 300 baud naar de Raspberry PI3, zodra een frequentie wordt ontvangen ( in het formaat xxxx.xx ( 2 cijfers achter de comma ), dwz per 100 Khz ) worden de juiste register waardes berekend om de ADF4351 in te stellen. Het programma op de Raspberry pi-3 genaamd serpush.c ontvangt ook of de VCO gelocked is en als ie gelocked is schrijft ie een 1 in de file ~/lock. Dus in 1 sessie wordt frequentie doorgegeven aan de ATMEGA8 en de ATMEGA8 stuurt de lock status weer terug. Dat is alles.

Sources

Source serpush.c, compileren dmv commando = cc serpush.c -o serpush

    #include <stdio.h>
    #include <string.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <errno.h>
    #include <termios.h>

    int main(int argc, char ** argv) {

      int fdr;

      fdr = open("/home/pi/freq",O_RDONLY); 
      if (fdr == -1) {
        perror("open_port: Unable to open /home/pi/freq - ");
        return(-1);
      }
      char buf1[256];
      int n1; 
      n1 = read(fdr, (void*)buf1, 255);
      if (n1 < 0) {
        perror("Read failed - ");
        return -1;
      } else if (n1 == 0) printf("No data on port\n");
      else {
        buf1[n1] = '\0';
        printf("%i bytes read : %s", n1, buf1);
      }

      int fd = open_port();
      change_bps(fd); 

    // Write to the port
      int n = write(fd,buf1,9);
      if (n < 0) {
        perror("Write failed - ");
        return -1;
      }

      // Read up to 255 characters from the port if they are there
      char buf[256];
      n = read(fd, (void*)buf, 255);
      if (n < 0) {
        perror("Read failed - ");
        return -1;
      } else if (n == 0) printf("No data on port\n");
      else {
        buf[n] = '\0';
        printf("%i bytes read : %s", n, buf);
      }

      // Don't forget to clean up
      close(fd);
      close(fdr);

}

void change_bps(int fd)
{
    struct termios options;

    /*
     * Get the current options for the port...
     */

    tcgetattr(fd, &options);


    /*
     * Set the baud rates to ...
     */

        cfsetispeed(&options, B300);
        cfsetospeed(&options, B300);
        tcsetattr(fd, TCSADRAIN, &options);

}

    int
    open_port(void)
    {
      int fd; /* File descriptor for the port */
      fd = open("/dev/ttyAMA0", O_RDWR | O_NOCTTY | O_NDELAY);
      if (fd == -1)
      {
        perror("open_port: Unable to open /dev/ttyS0 - ");
      }
      else
        fcntl(fd, F_SETFL, 0);
      return (fd);
    }



Python besturing source, hiermee kun je dmv de muis of touchscreen de juiste stream selecteren en de juiste frequentie instellen. :



import Tkinter
import subprocess
import time

from Tkinter import *
from Tkinter import BOTH, W, NW, SUNKEN, TOP, X, FLAT, LEFT

top = Tkinter.Tk()

time1 = ''
clock = Label(top, font=('Purisa', 12), bg='powder blue')
clock.pack(fill=BOTH, expand=1)

def tick():
    global time1
    time2 = time.strftime('%H:%M:%S')
    if time2 != time1:
        time1 = time2
        clock.config(text=time2)
    clock.after(200, tick)

def helloCallBack():
   subprocess.call('/home/pi/Desktop/436000', shell=True)
   text = openInstrucktion()
   W.insert("1.0", text)
   W.insert("1.10", " MHz")
   resultaat = lock()
   if resultaat == "LOCKED":
       W.tag_add("here","1.0", END)
       W.tag_config("here", background = "green2")
   else:
       W.tag_add("here","1.0", END)
       W.tag_config("here", background = "red")
	
def helloCallBack2():
   subprocess.call('/home/pi/Desktop/1257000', shell=True)
   text = openInstrucktion()
   W.insert("1.0", text)
   W.insert("1.10", " MHz")
   resultaat = lock()
   if resultaat == "LOCKED":
       W.tag_add("here","1.0", END)
       W.tag_config("here", background = "green2")
   else:
       W.tag_add("here","1.0", END)
       W.tag_config("here", background = "red")

def helloCallBack3():
   subprocess.call('/home/pi/Desktop/1260000', shell=True)
   text = openInstrucktion()
   W.insert("1.0", text)
   W.insert("1.10", " MHz")
   resultaat = lock()
   if resultaat == "LOCKED":
       W.tag_add("here","1.0", END)
       W.tag_config("here", background = "green2")
   else:
       W.tag_add("here","1.0", END)
       W.tag_config("here", background = "red")

def helloCallBack4():
   subprocess.call('/home/pi/Desktop/1252500', shell=True)
   text = openInstrucktion()
   W.insert("1.0", text)
   W.insert("1.10", " MHz")
   resultaat = lock()
   if resultaat == "LOCKED":
       W.tag_add("here","1.0", END)
       W.tag_config("here", background = "green2")
   else:
       W.tag_add("here","1.0", END)
       W.tag_config("here", background = "red")

def helloCallBack5():
   subprocess.call('/home/pi/Desktop/sr2000-X11', shell=True)
   text2 = openInstrucktion2()
   V.insert("1.0", text2)
   V.tag_configure("center", justify='center')
   V.tag_add("center", 1.0, "end")
   V.insert(END,text2, "center")
   F.configure(background = "orange")
   G.configure(background = "light grey")
   H.configure(background = "light grey")
   I.configure(background = "light grey")
   J.configure(background = "light grey")
   K.configure(background = "light grey")
   L.configure(background = "light grey")

def helloCallBack6():
   subprocess.call('/home/pi/Desktop/sr2000-testbeeld', shell=True)
   text2 = openInstrucktion2()
   V.insert("1.0", text2)
   V.tag_configure("center", justify='center')
   V.tag_add("center", 1.0, "end")
   V.insert(END,text2, "center")
   F.configure(background = "light grey")
   G.configure(background = "orange")
   H.configure(background = "light grey")
   I.configure(background = "light grey")
   J.configure(background = "light grey")
   K.configure(background = "light grey")
   L.configure(background = "light grey")

def helloCallBack7():
   subprocess.call('/home/pi/Desktop/sr2000-testbeeld-hamradio', shell=True)
   text2 = openInstrucktion2()
   V.insert("1.0", text2)
   V.tag_configure("center", justify='center')
   V.tag_add("center", 1.0, "end")
   V.insert(END,text2, "center")
   F.configure(background = "light grey")
   G.configure(background = "light grey")
   H.configure(background = "orange")
   I.configure(background = "light grey")
   J.configure(background = "light grey")
   K.configure(background = "light grey")
   L.configure(background = "light grey")


def helloCallBack8():
   subprocess.call('/home/pi/Desktop/sr2083-X11', shell=True)
   text2 = openInstrucktion2()
   V.insert("1.0", text2)
   V.tag_configure("center", justify='center')
   V.tag_add("center", 1.0, "end")
   V.insert(END,text2, "center")
   F.configure(background = "light grey")
   G.configure(background = "light grey")
   H.configure(background = "light grey")
   I.configure(background = "orange")
   J.configure(background = "light grey")
   K.configure(background = "light grey")
   L.configure(background = "light grey")


def helloCallBack9():
   subprocess.call('/home/pi/Desktop/sr2083-testbeeld', shell=True)
   text2 = openInstrucktion2()
   V.insert("1.0", text2)
   V.tag_configure("center", justify='center')
   V.tag_add("center", 1.0, "end")
   V.insert(END,text2, "center")
   F.configure(background = "light grey")
   G.configure(background = "light grey")
   H.configure(background = "light grey")
   I.configure(background = "light grey")
   J.configure(background = "orange")
   K.configure(background = "light grey")
   L.configure(background = "light grey")


def helloCallBack10():
   subprocess.call('/home/pi/Desktop/sr250-X11-fec12', shell=True)
   text2 = openInstrucktion2()
   V.insert("1.0", text2)
   V.tag_configure("center", justify='center')
   V.tag_add("center", 1.0, "end")
   V.insert(END,text2, "center")
   F.configure(background = "light grey")
   G.configure(background = "light grey")
   H.configure(background = "light grey")
   I.configure(background = "light grey")
   J.configure(background = "light grey")
   K.configure(background = "orange")
   L.configure(background = "light grey")


def helloCallBack11():
   subprocess.call('/home/pi/Desktop/sr250-X11-fec78', shell=True)
   text2 = openInstrucktion2()
   V.insert("1.0", text2)
   V.tag_configure("center", justify='center')
   V.tag_add("center", 1.0, "end")
   V.insert(END,text2, "center")
   F.configure(background = "light grey")
   G.configure(background = "light grey")
   H.configure(background = "light grey")
   I.configure(background = "light grey")
   J.configure(background = "light grey")
   K.configure(background = "light grey")
   L.configure(background = "orange")


def openInstrucktion():
    f= open("/home/pi/freq")
    text = f.read()
    return text
def openInstrucktion2():
    f= open("/home/pi/rate")
    text2 = f.read()
    return text2

def lock():
    f= open("/home/pi/lock")
    text3 = f.read()
    if text3 == "1\n":
    	resultaat = "LOCKED"
    else:
    	resultaat = "UNLOCKED"
    return resultaat


def blink(rectangle, canvas):
    for i in range(3):
        canvas.itemconfigure(rectangle, fill = "red")
        sleep(1)
        canvas.itemconfigure(rectangle, fill = "white")
        sleep(1)



def helloCallBack99():
   subprocess.call('/home/pi/Desktop/stop', shell=True)
   text2 = openInstrucktion2()
   V.insert("1.0", text2)
   V.tag_configure("center", justify='center')
   V.tag_add("center", 1.0, "end")
   V.insert(END,text2, "center")
   F.configure(background = "light grey")
   G.configure(background = "light grey")
   H.configure(background = "light grey")
   I.configure(background = "light grey")
   J.configure(background = "light grey")
   K.configure(background = "light grey")
   L.configure(background = "light grey")


canvas = Canvas(width = 300, height = 50, bg = 'powder blue')
canvas.pack(expand = YES, fill = BOTH)
canvas.create_text(5,25 ,anchor= "w" ,font=("Purisa",20), text= "PE2JKO, D-ATV Raspberry PI 3")

B = Tkinter.Button(top, text ="436 MHz", font=("Helvetica",12), command = helloCallBack)
C = Tkinter.Button(top, text ="1257 MHz", font=("Helvetica",12), command = helloCallBack2)
D = Tkinter.Button(top, text ="1260 MHz", font=("Helvetica",12), command = helloCallBack3)
E = Tkinter.Button(top, text ="1252.60 MHz", font=("Helvetica",12), command = helloCallBack4)
F = Tkinter.Button(top, text ="2000KS/s X11", font=("Helvetica",14), command = helloCallBack5)
G = Tkinter.Button(top, text ="2000KS/s TESTBEELD", font=("Helvetica",14), command = helloCallBack6)
H = Tkinter.Button(top, text ="2000KS/s TESTBEELD HAMRADIO", font=("Helvetica",14), command = helloCallBack7)
I = Tkinter.Button(top, text ="2083KS/s X11", font=("Helvetica",14), command = helloCallBack8)
J = Tkinter.Button(top, text ="2083KS/s TESTBEELD", font=("Helvetica",14), command = helloCallBack9)
K = Tkinter.Button(top, text ="250 KS/s X11 FEC 1/2", font=("Helvetica",14), command = helloCallBack10)
L = Tkinter.Button(top, text ="250 KS/s X11 FEC 7/8", font=("Helvetica",14), command = helloCallBack11)
Z = Tkinter.Button(top, text ="STOP", fg="red", font=("Helvetica-bold",18),command = helloCallBack99)
text = openInstrucktion()
text2 = openInstrucktion2()

resultaat = lock()
if resultaat == "LOCKED":
   W = Text(top ,height=1, bg = "green2", width=11,font=("Helvetica",32))
else:
   W = Text(top ,height=1, bg = "red", width=11,font=("Helvetica",32))
W.insert("1.0", text)
W.insert("1.10", " MHz")

V = Text(top ,height=1, bg = "light grey", fg = "blue", width=18,font=("Helvetica",28))
V.tag_configure("center", justify='center')
V.tag_add("center", 1.0, "end")
V.insert(END,text2, "center")
W.pack()
V.pack()
F.pack(fill=X)
G.pack(fill=X)
H.pack(fill=X)
I.pack(fill=X)
J.pack(fill=X)
K.pack(fill=X)
L.pack(fill=X)
Z.pack(fill=X)
B.pack(in_=top, side=LEFT)
C.pack(in_=top, side=LEFT)
D.pack(in_=top, side=LEFT)
E.pack(in_=top, side=LEFT)

tick()
top.mainloop()



Voorbeeldscript om stream aan te roepen en in een controlfile de naam te plaatsen :



#!/bin/sh
#
sudo /home/pi/stop
echo "2000 Ks/s, FEC 1/2" > /home/pi/rate
/home/pi/sr2000-x11-direct




Voorbeeldscript om frequentie aan te passen :



#!/bin/sh

echo "436.00" > /home/pi/freq
/home/pi/serpush | tail -c 2 | cat - > /home/pi/lock
sleep 1
/home/pi/serpush | tail -c 2 | cat - > /home/pi/lock
exit




Voorbeeldscript om stream op te starten, in dit geval een stukje desktop (X11grab) (opgepast is een hele lange string!!, berekend voor 250Ks/s) :



#!/bin/sh

mkfifo /tmp/video2.ts

sudo nice -n -30 arecord -f S16_LE -r 48000 -c 1 -M -D plughw:1 | sudo nice -n -30 ffmpeg -itsoffset -00:00:0.8 -analyzeduration 0 -probesize 2048 -fpsprobesize 0 -ac 1 -thread_queue_size 512 -i - -f x11grab -i :0.0+0,0 -framerate 25 -video_size 300x288 -fflags nobuffer -s 300x288 -b:v 200k -minrate:v 200k -maxrate:v 200k -vcodec mpeg2video -blocksize 1880 -strict experimental -async 2 -acodec mp2 -ab 32K -ar 48k -ac 1 -flags -global_header -f mpegts -blocksize 1880 -mpegts_original_network_id 1 -mpegts_transport_stream_id 1 -mpegts_service_id 1 -mpegts_pmt_start_pid 1001 -mpegts_start_pid 101 -metadata service_provider=PE2JKO -metadata service_name=PE2jko -muxrate 230392 -y /tmp/video2.ts &

sudo /home/pi/rpidatv/bin/rpidatv -i /tmp/video2.ts -s 250 -c 1/2 -m IQ -f 0 -x 12 -y 13 &

exit 0