Antenna Arrays And Python – Square Patch Element

As mentioned in my intro post, the individual antennas in an array are often known as “elements”. To compute the arrays performance each individual elements field contribution needs to be summed. For my initial investigation I focus on using a rectangular microstrip patch element and this post will cover the model that is used.

A microstrip or patch antenna is a low ­profile antenna that has a number of advantages over other antennas – it is lightweight, inexpensive, and easy to integrate with accompanying electronics because they can be printed directly onto a circuit board which makes them easy to fabricate.

A patch antenna usually consists of a conductive patch with width W, and length L, sitting on top of a substrate (i.e. a dielectric circuit board) with thickness h and relative permittivity Er. The substrate then sits on top of a conductive ground plane.

 

Patch Antenna (taken from ArrayCalc)
The element model is taken from C.A. Balanis 2nd Edition Page 745, and represents the far-field element radiation patterns as closed form mathematical equations. The model used is the cavity/transmission-line model and is referenced by most antenna texts covering microstrip antennas. The patch is modelled as 2 radiating slots, separated by a nominally half wavelength section of low impedance transmission line. The calculations are fully detailed in the ArrayCalc Design_patchr.m file and also the theory is detailed in the Theory Of Operation document. I also find the antenna-theory website a useful resource for further reading.

With the model used there is no account taken of mutual coupling between the elements. This can have a significant effect on array performance when array elements themselves are large, elements are closely spaced or large scan angles are used. It is also assumed the ground plane is infinite so the model is only valid over 0°<theta<90°, 0°<phi < 360°. The benefits of this model are the calculation is potentially very fast and despite the limitations the model still provides enough accuracy to give a useful insight into the potential performance of an array, before committing to more detailed modelling or prototyping.

The E theta and E phi components of the far-field radiation pattern are given by the equations below:

Using these equations allows us to create the following Python function to calculate the total E-field pattern for the patch as a function of theta and phi:

Before moving on to the array calculation itself next time I’ll detail the Python scripts I use to visualise the fields as it’s always nice to see what’s going on.

Deploying a Django app to Heroku

Note to self — before deploying a local Django app to Heroku I should do the following:

Install the wsgi HTTP server, gunicorn:

pip install gunicorn

Install package to help work with environment variables:

pip install python-dotenv

Install Python-PostgreSQL Database Adapter:

pip install psycopg2

Create requirements file:

pip freeze > requirements.txt

Configure settings.py to load environment variables and the database:

import dotenv
import dj_database_url

# this line is already in your settings.py
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

# load environment variables from .env
dotenv_file = os.path.join(BASE_DIR, ".env")
if os.path.isfile(dotenv_file):
    dotenv.load_dotenv(dotenv_file)

# load database from the DATABASE_URL environment variable
DATABASES = {}
DATABASES['default'] = dj_database_url.config(conn_max_age=600)

Also delete the default database config:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}

Add a .env file to project base directory and add the database URL:

DATABASE_URL=sqlite:///db.sqlite3

Make sure .env is added to .gitignore so it doesn’t get pushed to repo. My last .gitignore looked like:

*.pyc
*~
__pycache__
myvenv
venv
db.sqlite3
.DS_Store
.env

Assuming there is already a heroku account run:

heroku login

locally to associate machine with heroku account.

On the heroku dashboard create a new app. Under the “Settings” tab and under “Buildpacks” add a new buildpack called “heroku/python”.

Under “Config Variables” add “DISABLE_COLLECTSTATIC” with a value of “1”.

To set-up the database go back to command line and enter:

heroku addons:create heroku-postgresql:hobby-dev -a YOUR_APP_NAME

This provisions a new postgres database and should add the url to the Config Variables. Run command:

heroku config -a YOUR_APP_NAME

DATABASE_URL should be added to Config Variables.

Now create a new file called Procfile in the base directory of project and add:

web: gunicorn YOURSITE.wsgi --log-level=info --log-file -

Where YOURSITE is specific to the project.

Add another file in base directory called runtime.txt and add the version of python you want to use:

python-3.5.2

To serve static assets I use the WhiteNoise project. To install:

pip install whitenoise
...
pip freeze > requirements.txt

Add it to the app by changing wsgi.py to include:

from django.core.wsgi import get_wsgi_application
from whitenoise.django import DjangoWhiteNoise

application = get_wsgi_application()
application = DjangoWhiteNoise(application)

Now that should be it! Commit the changes locally then run:

git push heroku master

It should display the progress of installation and eventually be up and running.

References

This was trying to put a bunch of stuff I read in the articles below into an order I’ve found works for me.

https://blog.heroku.com/in_deep_with_django_channels_the_future_of_real_time_apps_in_django

Updating distributed RaspberryPI’s with automatic code updates

I’m working on a project that has multiple RaspberryPI’s, distributed in multiple locations, but all running the same code (Django Channels with Python background worker scripts). I wanted to be able to deploy any updates to the main code to all the PI’s with as minimal manual interaction as possible. I couldn’t find an obvious way to do this so thought I’d summarise what I’ve set-up in the hope I may get some feedback or it would help someone else.

Basic overview of solution

The main code base is stored in a Git repository in BitBucket.

I have a development PI set-up and when I’m ready I push code changes from it to the main branch of the BitBucket repo.

Each deployed PI has an updateWorker Python script running that periodically downloads the remote code from the BitBucket repository using the git fetch command. It then checks for changes using the git status command and finally updates the code if there are changes using git reset.

Running Git from Python

To run the Git commands from my Python script I’m using a Python Package called sh. It functions as a subprocess replacement that allows you to call any program as if it were a function. I plan to write another post about using it but the basics are intuitive. For example to do a git status call on the local directory and save the response in a variable called statusCheck:

import sh
from sh import git

statusCheck = git("status")

(See this tutorial for more details on how the login to BitBucket is handled)

updateWorker Script

import sh
from sh import git
import time
import os, sys

aggregated = ""

def CheckForUpdate(workingDir):
    print("Fetching most recent code from source..." + workingDir)

    # Fetch most up to date version of code.
    p = git("--git-dir=" + workingDir + ".git/", "--work-tree=" + workingDir, "fetch", "origin", "master", _out=ProcessFetch, _out_bufsize=0, _tty_in=True)               
    
    print("Fetch complete.")
    time.sleep(2)
    print("Checking status for " + workingDir + "...")

    statusCheck = git("--git-dir=" + workingDir + ".git/", "--work-tree=" + workingDir, "status")

    if "Your branch is up-to-date" in statusCheck:
        print("Status check passes.")
        print("Code up to date.")
        return False
    else:
        print("Code update available.")
        return True

def ProcessFetch(char, stdin):
    global aggregated

    sys.stdout.flush()
    aggregated += char
    if aggregated.endswith("Password for 'https://yourrepo@bitbucket.org':"):
        print(mainLogger, "Entering password...", True)
        stdin.put("yourpassword\n")

if __name__ == "__main__":
    checkTimeSec = 60
    gitDir = "/var/testupdate/"
   
    while True:
        print("*********** Checking for code update **************")                                                     
    
        if CheckForUpdate(gitDir):
            print("Resetting code...")
            resetCheck = git("--git-dir=" + gitDir + ".git/", "--work-tree=" + gitDir, "reset", "--hard", "origin/master")
            print(str(resetCheck)) 
                                                                                                                                                                
        print("Check complete. Waiting for " + str(checkTimeSec) + "seconds until next check...", True)
        time.sleep(checkTimeSec)

updateWorker uses the –git-dir and –work-tree options to define where the local git repo is for the code.

The script parses the response from the git status command to determine if there are updates. If an update is available we will see:

On branch master

Your branch is behind ‘origin/master’ by 2 commits, and can be fast-forwarded.
(use “git pull” to update your local branch)
nothing to commit, working directory clean

On my PIs the updateWorker then kills all the running scripts which are automatically restarted by another background process, this should mean the scripts are all running up to date versions. So far it’s working well but not sure if I’m doing something crazy wrong!

Git calls on another directory

Today I learned that it’s possible to call git commands on a directory/repository other than the one you are currently in. For example to call git status on a repo in /var/app just type:

git --git-dir=/var/app/.git/ --work-tree=/var/app status

I used this in a Python script I’ve been writing at work that automatically checks for updates in a repo and updates the code if there are any. More on that another time.