Montag, 24. Mai 2021

Eine drehbare Multibandantenne

Um für meine SDR Anlage insbesondere den Empfang von Langwellen zu ermöglichen, ist die Idee entstanden eine drehbare Multibandantenne zu konstruieren. Da der Platz unter dem Garagendach begrenzt ist, habe ich mich für eine magnetische loop Antenne entschieden.

Die Rahmenantenne hat eine Diagonale von 50cm. Das Funktionsprinzip der Antenne beruht darauf die Drahtwicklungen mit einem Drehkondensator abzugleichen, und dann mit einer einzigen Windung das Signal auszukoppeln. Die Windungen der Spule werden mit 0,6 mm Kupferlackdraht und die Auskoppelwindung mit einfachem Klingeldraht ausgeführt. Das Bild unten zeigt den Schaltplan schematisch.

Mit Hilfe zweier Schaltrelais lassen sich mehrere Windungen kombinieren, so dass Empfang von Kurz-, Mittel- und Langwelle möglich ist. Dem Drehkondensator kann ein weiterer, fester Kondensator mit einem dritten Relais parallel geschaltet werden, um so innerhalb einen Bandes das obere bzw untere Frequenzband erreichen zu können. 

Die Berechnungen mit dem Magnet-Loopantennenrechner von DG0KW seien hier exemplarisch für Mittelwelle vorgestellt. Bei 10 Windungen und der oberen Bandgrenze von 1,5MHz reicht die Eigenkapazität der Antenne als Kondensator.

Um die untere Bandgrenze bei 0,5MHz zu erreichen muss ein Kondensator von ~500pF parallel zum 500pF Drehko geschaltet werden

Das nächste Bild zeigt die Antenne von hinten. Im unteren Teil sind die drei bereits beschriebenen Relais, die das Umschalten zwischen Lang-, Mittel-, und Kurzwelle sowie zwischen High- und Lowband ermöglichen zu sehen. Ein Schrittmotor dreht den 500pF VariCap. Auch der dazugehörige Motortreiber wurde auf die Rückseite montiert. 

Die Antenne ist drehbar gelagert, und kann mit einem weiteren Schrittmotor über einen Kettenriemen um bis zu 90° gedreht werden. Ein Messingrohr dient als mechanischer Anschlag und zugleich als Schaltkontakt um die Nullposition der Antenne erkennen zu können. 

Im montierten Zustand sieht das Ganze dann so aus.

Angesteuert wird die Antenne über ein Phyton Script das auf dem Rasperberry der SDR Anlage läuft. Nach Aufruf des Programms wird zunächst sowohl der VariCap als auch die Antenne selbst in ihre Ausgangsposition gefahren. Die Schrittweite lässt sich im jeweiligen Eingabefeld einstellen, und durch Drücken auf die entsprechende Schaltfläche lassen sich die Kapazität bzw die Drehposition vergrößern oder verkleinern.

Die Verbindung zur GPIO Kontaktleiste des Raspberry erfolgt über zwei 8-polige Telefonkabel mit folgender Farbkodierung.

Abschließend sei noch das PythonScript zur Antennenkontrolle gezeigt. Viel Spaß damit...


#Note: Runs with phyton3 only

#!/usr/bin/python

import sys

import time

import RPi.GPIO as GPIO

from tkinter import *


GPIO.setmode(GPIO.BOARD)

GPIO.setwarnings(False)


#define settings for Relais

GPIO.setup(3,GPIO.OUT) #Relais 1 for KW/MW/LW band switch

GPIO.setup(5,GPIO.OUT) #Relais 2 for KW/MW/LW band switch

GPIO.setup(7,GPIO.OUT) #Relais 3 for Low/High band switch


#define settings of GUI

window = Tk()

window.title("Antenna Control Panel")


# define global variable for last VariCap motor step

VariCapSeqPos=0

# define global variable for last Antenna motor step

AntennaSeqPos=0

# define global variable for current capacitance of VariCap

TotalCapa=0

# define global variable of antenna position

AntennaPos=0


# Reminder to Reset VariCap and Antenna at start of Program in case of no auto reset

# ResetRemind_label = Label(window, text="Please reset Varicap and Antenna before start...", foreground="red")

ResetRemind_label = Label(window, text="")


#define Reset Vari Cap button

def ResetVariCap():

    global VariCapSeqPos

    #run stepper full speed >540pF counter clock wise until stop signal (pin16) appears

    VariCapSeqPos=MotorVariCap(1, 550, -1, VariCapSeqPos)

    global TotalCapa

    TotalCapa=0

    TotalCapa_label.config(text="Total Capa 0 pF", foreground="black" )

    ResetRemind_label.config(text="")

    

ResetVariCap_button = Button(window, text="Reset VariCap", foreground="black", command=lambda: ResetVariCap())


#define Reset Antenna Rotation button

def ResetAntenna():

    global AntennaSeqPos

    #run stepper full speed >90 deg counter clock wise until stop signal (pin29) appears

    AntennaSeqPos=MotorAntenna(2, 95, -1, AntennaSeqPos)

    global AntennaPos

    AntennaPos=0

    AntennaPos_label.config(text="Antenna Position 0 deg", foreground="black" )

    ResetRemind_label.config(text="")

    

ResetAntennaRot_button = Button(window, text="Reset Antenna Rotation", foreground="black", command=lambda: ResetAntenna() )



#define KW/MW/LW switch

def setLW():

   GPIO.output(3, False)

   GPIO.output(5, False)


def setMW():

   GPIO.output(3, False)

   GPIO.output(5, True)


def setKW():

   GPIO.output(3, True)

   GPIO.output(5, False)


i = IntVar() #Link Radiobutton With The Variable=i.

i.set(3) #'LW' is default value

setLW

# Disconnect coil 2 and 3 for 'KW'

KW_radiobutton = Radiobutton(window, text="KW", value=1, variable=i, command=setKW)

# Disconnect coil 3 for 'MW'

MW_radiobutton = Radiobutton(window, text="MW", value=2, variable=i, command=setMW)

# Use all coils for 'LW'

LW_radiobutton = Radiobutton(window, text="LW", value=3, variable=i, command=setLW)

    

#define HighBand/LowBand switch

j = IntVar() #Link Radiobutton With The Variable=j.

j.set(1) # 'Low Band' is default value

GPIO.output(7, True)

#Low Band, pin7 (Relais 3) on, additional Capa parallel to VariCap

LowBand_radiobutton = Radiobutton(window, text="Low Band", foreground="black", value=1, variable=j, command=lambda: GPIO.output(7, True))

#High Band, pin7 (Relais 3) off, no additional Capa

HighBand_radiobutton = Radiobutton(window, text="High Band", foreground="black", value=2, variable=j, command=lambda: GPIO.output(7, False))



# Define capa input box

ChangeCapa_label = Label(window, text="Change Capa by (pF) ", foreground="black" )

CapaInputBox = Entry(window, bd=5, width=10)

TotalCapa_label = Label(window)


def ChangeCapaUp_action():

    entry_text = CapaInputBox.get()

    if entry_text.isdigit():

        entry=int(entry_text)

        #global TotalCapa

        #TotalCapa += entry

        #move motor slowly clock wise

        global VariCapSeqPos

        VariCapSeqPos=MotorVariCap(7, entry, 1, VariCapSeqPos)

        TotalCapa_label.config(text="Total Capa " + str(round(TotalCapa)) + " pF", foreground="black")

        

def ChangeCapaDown_action():

    entry_text = CapaInputBox.get()

    if entry_text.isdigit():

        entry=int(entry_text)

        #move motor slowly clock wise

        global VariCapSeqPos

        VariCapSeqPos=MotorVariCap(7, entry, -1, VariCapSeqPos)

        #global TotalCapa

        #TotalCapa -= entry

        TotalCapa_label.config(text="Total Capa " + str(round(TotalCapa)) + " pF", foreground="black")


# Define CapaChangeUp button

CapaChangeUp_button = Button(window, text="Up", foreground="black", command=ChangeCapaUp_action)

# Define CapaChangeDown button

CapaChangeDown_button = Button(window, text="Down", foreground="black", command=ChangeCapaDown_action)


# Define AntennaRot input box

AntennaRot_label = Label(window, text="Rotate Antenna by (deg) ", foreground="black")

AntennaInputBox = Entry(window, bd=5, width=10)

AntennaPos_label = Label(window)


def ChangeAntennaUp_action():

    entry_text = AntennaInputBox.get()

    if entry_text.isdigit():

        entry=int(entry_text)

        #move motor slowly clock wise

        global AntennaSeqPos

        AntennaSeqPos=MotorAntenna(7, entry, 1, AntennaSeqPos)

        AntennaPos_label.config(text="Antenna Position " + str(round(AntennaPos)) + " deg", foreground="black")

        

def ChangeAntennaDown_action():

    entry_text = AntennaInputBox.get()

    if entry_text.isdigit():

        entry=int(entry_text)

        #move motor slowly clock wise

        global AntennaSeqPos

        AntennaSeqPos=MotorAntenna(7, entry, -1, AntennaSeqPos)

        AntennaPos_label.config(text="Antenna Position " + str(round(AntennaPos)) + " deg", foreground="black")


# Define AntennaChangeUp button

AntennaChangeUp_button = Button(window, text="Up", foreground="black", command=ChangeAntennaUp_action)

# Define AntennaChangeDown button

AntennaChangeDown_button = Button(window, text="Down", foreground="black", command=ChangeAntennaDown_action)#


# Define 'Exit' button

Exit_button = Button(window, text="Quit", foreground="black", command=window.quit)



# Arrange components in window

ResetRemind_label.grid(sticky="W", row=0, column=0)

ResetVariCap_button.grid(sticky="W", row=1, column =0)

ResetAntennaRot_button.grid(sticky="W", row=1, column =1)

KW_radiobutton.grid(sticky="W", row=2, column =0)

MW_radiobutton.grid(sticky="W", row=2, column=1)

LW_radiobutton.grid(sticky="W", row=2, column=3)

HighBand_radiobutton.grid(sticky="W", row=3, column =0)

LowBand_radiobutton.grid(sticky="W", row=3, column=1)

ChangeCapa_label.grid(sticky="W", row = 4, column = 0)

CapaInputBox.grid(sticky="W", row = 4, column = 1)

CapaChangeUp_button.grid(sticky="W", row = 4, column = 2)

CapaChangeDown_button.grid(sticky="W", row = 4, column = 3)

TotalCapa_label.grid(sticky="W", row = 5, column = 0, columnspan = 2)

AntennaRot_label.grid(sticky="W", row = 6, column = 0)

AntennaInputBox.grid(sticky="W", row = 6, column = 1)

AntennaChangeUp_button.grid(sticky="W", row = 6, column = 2)

AntennaChangeDown_button.grid(sticky="W", row = 6, column = 3)

AntennaPos_label.grid(sticky="W", row = 7, column = 0, columnspan = 3)

Exit_button.grid(sticky="W", row = 8, column = 4)



#define settings for VariCap StepperMotor

aVariCapMotorPins = [12, 15, 11, 13]

 

# Set all pins as output

for pin in aVariCapMotorPins:

    GPIO.setup(pin,GPIO.OUT)

    GPIO.output(pin, False)

    

#switch 16 against GND on VariCap

GPIO.setup(16, GPIO.IN, pull_up_down = GPIO.PUD_UP)


#define settings for Antenna Motor

aAntennaMotorPins = [31, 33, 35, 37]

 

# Set all pins as output

for pin in aAntennaMotorPins:

    GPIO.setup(pin,GPIO.OUT)

    GPIO.output(pin, False)

    

#switch 29 against GND on Antenna

GPIO.setup(29, GPIO.IN, pull_up_down = GPIO.PUD_UP)


aSequence = [

    [1,0,0,1],

    [1,0,0,0],

    [1,1,0,0],

    [0,1,0,0],

    [0,1,1,0],

    [0,0,1,0],

    [0,0,1,1],

    [0,0,0,1]

]


def MotorVariCap(speed, cap, iDirection, start):

    global TotalCapa

    global aSequence

    global aVariCapMotorPins

    

    iNumSteps = len(aSequence)

    #waiting time in between steps

    fWaitTime = int(speed) / float(1000)

    

    # 1024 steps is 90 degrees, 5416 steps is max capa of 540pF

    iSteps = int(int(cap) * 10.0293411)


    # motor starts at a specific position from the aSequence list

    iVariCapSeqPos = int(start)


    for step in range(0,iSteps):

        if GPIO.input(16) == GPIO.LOW and iDirection == -1:

            break

        if TotalCapa > 540:

            TotalCapa = 540

            break


        for iPin in range(0, 4):

            iRealPin = aVariCapMotorPins[iPin]

            if aSequence[iVariCapSeqPos][iPin] != 0:

                GPIO.output(iRealPin, True)

            else:

                GPIO.output(iRealPin, False)

     

        iVariCapSeqPos += iDirection

 

        if (iVariCapSeqPos >= iNumSteps):

            iVariCapSeqPos = 0

        if (iVariCapSeqPos < 0):

            iVariCapSeqPos = iNumSteps + iDirection

        

        # Adjust TotalCapa

        if iDirection == 1:

            TotalCapa += (cap/iSteps)

        else:

            TotalCapa -= (cap/iSteps)

 

        # wait

        time.sleep(fWaitTime)


    for pin in aVariCapMotorPins:

        GPIO.output(pin, False)


    # Return the position from the aSequence list which should have been the

    # next position, if the previous loop was not ended

    return (iVariCapSeqPos)


def MotorAntenna(speed, deg, iDirection, start):

    global AntennaPos

    global aSequence

    global aAntennaMotorPins

    

    iNumSteps = len(aSequence)

    #waiting time in between steps

    fWaitTime = int(speed) / float(1000)

    

    # 1024 steps is 90 degrees of motor, 225 degrees of motor is 90 degrees of Antenna, 16/40

    iSteps = int(int(deg) * 28.4444444444)


    # motor starts at a specific position from the aSequence list

    iAntennaSeqPos = int(start)


    for step in range(0,iSteps):

        if GPIO.input(29) == GPIO.LOW and iDirection == -1:

            break

        if AntennaPos > 90:

            AntennaPos = 90

            break


        for iPin in range(0, 4):

            iRealPin = aAntennaMotorPins[iPin]

            if aSequence[iAntennaSeqPos][iPin] != 0:

                GPIO.output(iRealPin, True)

            else:

                GPIO.output(iRealPin, False)

     

        iAntennaSeqPos += iDirection

 

        if (iAntennaSeqPos >= iNumSteps):

            iAntennaSeqPos = 0

        if (iAntennaSeqPos < 0):

            iAntennaSeqPos = iNumSteps + iDirection

        

        # Adjust TotalCapa

        if iDirection == 1:

            AntennaPos += (deg/iSteps)

        else:

            AntennaPos -= (deg/iSteps)

 

        # wait

        time.sleep(fWaitTime)


    for pin in aAntennaMotorPins:

        GPIO.output(pin, False)


    # Return the position from the aSequence list which should have been the

    # next position, if the previous loop was not ended

    return (iAntennaSeqPos)



ResetRemind_label.config(text="Resetting VariCap...", foreground="red")

ResetVariCap()

ResetRemind_label.config(text="Resetting Antenna...", foreground="red")

ResetAntenna()

ResetRemind_label.config(text="")


mainloop()

Keine Kommentare:

Kommentar veröffentlichen