kopia lustrzana https://github.com/projecthorus/wenet
Many changes to tx code to better handle radio upsets. Move to libcamera2 as the default
rodzic
9959be4ccb
commit
3828ff03b5
91
start_tx.sh
91
start_tx.sh
|
@ -1,9 +1,9 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
#
|
#
|
||||||
# Wenet TX-side Initialisation Script
|
# Wenet TX-side Initialisation Script - Systemd Unit Version
|
||||||
# 2022-03-19 Mark Jessop <vk5qi@rfhead.net>
|
# 2024-07-21 Mark Jessop <vk5qi@rfhead.net>
|
||||||
#
|
#
|
||||||
# Run this to set up an attached RFM22B/RFM98W and start transmitting!
|
# Run this to set up an attached RFM98W and start transmitting!
|
||||||
# Replace the transmit frequency and callsign with your own.
|
# Replace the transmit frequency and callsign with your own.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
@ -11,31 +11,92 @@
|
||||||
# This MUST be <= 6 characters long.
|
# This MUST be <= 6 characters long.
|
||||||
MYCALL=N0CALL
|
MYCALL=N0CALL
|
||||||
|
|
||||||
# The centre frequency of the Wenet transmission.
|
# The centre frequency of the Wenet transmission, in MHz.
|
||||||
TXFREQ=443.500
|
TXFREQ=443.500
|
||||||
|
|
||||||
|
# Transmit power, in dBm
|
||||||
|
# Allowed values are from 2 through 17 dBm.
|
||||||
|
TXPOWER=17
|
||||||
|
|
||||||
|
# GPS Port
|
||||||
|
# Note that we only support uBlox GPS units
|
||||||
|
# set this to none to disable GPS support
|
||||||
|
GPSPORT=/dev/ttyACM0
|
||||||
|
|
||||||
|
# Image settings
|
||||||
|
# Image scaling - Scale the 'native' image resolution of the attached camera by this much
|
||||||
|
# before transmitting.
|
||||||
|
TX_IMAGE_SCALING=0.5
|
||||||
|
|
||||||
|
# White Balance settings
|
||||||
|
# Allowed Values: Auto, Daylight, Cloudy, Incandescent, Tungesten, Fluorescent, Indoor
|
||||||
|
WHITEBALANCE=Auto
|
||||||
|
|
||||||
|
# Refer near the end of this file for image flipping and overlay options
|
||||||
|
|
||||||
# Baud Rate
|
# Baud Rate
|
||||||
# Known working transmit baud rates are 115200 (the preferred default).
|
# Known working transmit baud rates are 115200 (the preferred default).
|
||||||
# Lower baud rates *may* work, but will need a lot of testing on the receiver
|
# Lower baud rates *may* work, but will need a lot of testing on the receiver
|
||||||
# chain to be sure they perform correctly.
|
# chain to be sure they perform correctly.
|
||||||
BAUDRATE=115200
|
BAUDRATE=115200
|
||||||
|
|
||||||
# GPS Port
|
# RFM98W SPI Device
|
||||||
# Note that we only support uBlox GPS units
|
# SPI device number of your RFM98W chip
|
||||||
GPSPORT=/dev/ttyACM0
|
# This will either be 0 or 1 on a RPi.
|
||||||
|
SPIDEVICE=0
|
||||||
|
|
||||||
|
# Modulation UART
|
||||||
|
# The UART used to modulate the RFM98W with our Wenet transmission
|
||||||
|
# We want to be using the PL011 UART, *not* the Mini-UART
|
||||||
|
# On a Pi Zero W, you may need to disable bluetooth. See here for more info:
|
||||||
|
# https://www.raspberrypi.com/documentation/computers/configuration.html#uarts-and-device-tree
|
||||||
|
SERIALPORT=/dev/ttyAMA0
|
||||||
|
|
||||||
|
|
||||||
# CHANGE THE FOLLOWING LINE TO REFLECT THE ACTUAL PATH TO THE TX FOLDER.
|
# CHANGE THE FOLLOWING LINE TO REFLECT THE ACTUAL PATH TO THE TX FOLDER.
|
||||||
# i.e. it may be /home/username/dev/wenet/tx/
|
# i.e. it may be /home/username/dev/wenet/tx/
|
||||||
cd /home/pi/wenet/tx/
|
cd /home/pi/wenet/tx/
|
||||||
|
|
||||||
#Uncomment to initialise a RFM22B (untested with Python 3)
|
# Wait here until the SPI devices are available.
|
||||||
#python init_rfm22b.py $TXFREQ
|
# This can take a few tens of seconds after boot.
|
||||||
# Uncomment for use with a RFM98W
|
timeout=20
|
||||||
python3 init_rfm98w.py --frequency $TXFREQ --baudrate $BAUDRATE
|
echo "Checking that the SPI devices exist..."
|
||||||
|
while : ; do
|
||||||
|
[[ -e "/dev/spidev0.0" ]] && break
|
||||||
|
|
||||||
|
if [ "$timeout" == 0 ]; then
|
||||||
|
echo "Did not find SPI device in timeout period!"
|
||||||
|
exit 1
|
||||||
|
# At this point this script exits, and systemd should restart us anyway.
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Waiting another 2 seconds for SPI to be available."
|
||||||
|
sleep 2
|
||||||
|
((timeout--))
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Waiting another 10 seconds before startup."
|
||||||
|
sleep 10
|
||||||
|
|
||||||
|
|
||||||
# Start the main TX Script.
|
# Start the main TX Script.
|
||||||
# Note that you can also add --logo /path/to/logo.png to this to add a logo overlay.
|
#
|
||||||
python3 tx_picam_gps.py --baudrate $BAUDRATE --gps $GPSPORT $MYCALL &
|
# Additional configuration lines you may wish to add or remove before the $CALLSIGN line may include:
|
||||||
|
# Flip the image vertically and horizontally (e.g. if the camera is mounted upside down)
|
||||||
|
# --vflip --hflip \
|
||||||
|
# Add a logo overlay in the bottom right of the image. This must be a transparent PNG file.
|
||||||
|
# --logo yourlogo.png \
|
||||||
|
# Set a fixed focus position on a PiCam v3 (NOTE: The Picamv3 focus drifts with temperature - beware!!!)
|
||||||
|
# --lensposition 0.5 \
|
||||||
|
|
||||||
# If you don't want any GPS overlays, you can comment the above line and run:
|
python3 tx_picamera2_gps.py \
|
||||||
# python WenetPiCam.py --baudrate $BAUDRATE $MYCALL &
|
--rfm98w $SPIDEVICE \
|
||||||
|
--baudrate $BAUDRATE \
|
||||||
|
--frequency $TXFREQ \
|
||||||
|
--serial_port $SERIALPORT \
|
||||||
|
--tx_power $TXPOWER \
|
||||||
|
--gps $GPSPORT \
|
||||||
|
--resize $TX_IMAGE_SCALING \
|
||||||
|
--whitebalance $WHITEBALANCE \
|
||||||
|
--vflip --hflip \
|
||||||
|
$MYCALL
|
||||||
|
|
|
@ -1,65 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
#
|
|
||||||
# Wenet TX-side Initialisation Script - Systemd Unit Version
|
|
||||||
# 2024-07-21 Mark Jessop <vk5qi@rfhead.net>
|
|
||||||
#
|
|
||||||
# Run this to set up an attached RFM22B/RFM98W and start transmitting!
|
|
||||||
# Replace the transmit frequency and callsign with your own.
|
|
||||||
#
|
|
||||||
|
|
||||||
# A callsign which will be included in the Wenet Packets.
|
|
||||||
# This MUST be <= 6 characters long.
|
|
||||||
MYCALL=VK5ARG
|
|
||||||
|
|
||||||
# The centre frequency of the Wenet transmission.
|
|
||||||
TXFREQ=443.500
|
|
||||||
|
|
||||||
# Baud Rate
|
|
||||||
# Known working transmit baud rates are 115200 (the preferred default).
|
|
||||||
# Lower baud rates *may* work, but will need a lot of testing on the receiver
|
|
||||||
# chain to be sure they perform correctly.
|
|
||||||
BAUDRATE=115200
|
|
||||||
|
|
||||||
# GPS Port
|
|
||||||
# Note that we only support uBlox GPS units
|
|
||||||
GPSPORT=/dev/ttyACM0
|
|
||||||
|
|
||||||
# CHANGE THE FOLLOWING LINE TO REFLECT THE ACTUAL PATH TO THE TX FOLDER.
|
|
||||||
# i.e. it may be /home/username/dev/wenet/tx/
|
|
||||||
cd /home/pi/wenet/tx/
|
|
||||||
|
|
||||||
# Wait here until the SPI devices are available.
|
|
||||||
# This can take a few tens of seconds after boot.
|
|
||||||
timeout=20
|
|
||||||
while : ; do
|
|
||||||
[[ -e "/dev/spidev0.0" ]] && break
|
|
||||||
|
|
||||||
if [ "$timeout" == 0 ]; then
|
|
||||||
echo "Did not find SPI device in timeout period!"
|
|
||||||
exit 1
|
|
||||||
# At this point this script exits, and systemd should restart us anyway.
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Waiting another 2 seconds for SPI to be available."
|
|
||||||
sleep 2
|
|
||||||
((timeout--))
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "Waiting another 10 seconds before startup."
|
|
||||||
sleep 10
|
|
||||||
|
|
||||||
#Uncomment to initialise a RFM22B (untested with Python 3)
|
|
||||||
#python init_rfm22b.py $TXFREQ
|
|
||||||
# Uncomment for use with a RFM98W
|
|
||||||
python3 init_rfm98w.py --frequency $TXFREQ --baudrate $BAUDRATE
|
|
||||||
|
|
||||||
|
|
||||||
# Start the main TX Script.
|
|
||||||
# Do not add a & on the end of these lines when running via systemd!
|
|
||||||
|
|
||||||
# Note that you can also add --logo /path/to/logo.png to this to add a logo overlay.
|
|
||||||
# If using a Picam HQ, add a --picamhq argument into this line before the --gps argument
|
|
||||||
python3 tx_picam_gps.py --baudrate $BAUDRATE --gps $GPSPORT $MYCALL
|
|
||||||
|
|
||||||
# If you don't want any GPS overlays, you can comment the above line and run:
|
|
||||||
# python WenetPiCam.py --baudrate $BAUDRATE $MYCALL
|
|
|
@ -28,6 +28,7 @@ from time import sleep
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from ldpc_encoder import *
|
from ldpc_encoder import *
|
||||||
|
from radio_wrappers import *
|
||||||
from queue import Queue
|
from queue import Queue
|
||||||
|
|
||||||
class PacketTX(object):
|
class PacketTX(object):
|
||||||
|
@ -75,23 +76,16 @@ class PacketTX(object):
|
||||||
|
|
||||||
# WARNING: 115200 baud is ACTUALLY 115386.834 baud, as measured using a freq counter.
|
# WARNING: 115200 baud is ACTUALLY 115386.834 baud, as measured using a freq counter.
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
serial_port="/dev/ttyAMA0",
|
# Radio wrapper, for radio setup and modulation.
|
||||||
serial_baud=115200,
|
radio,
|
||||||
|
# User callsign, should be used in the idle sequence, but currently isn't...
|
||||||
|
callsign="N0CALL",
|
||||||
payload_length=256,
|
payload_length=256,
|
||||||
fec=True,
|
fec=True,
|
||||||
debug = False,
|
|
||||||
callsign="N0CALL",
|
|
||||||
udp_listener = None,
|
udp_listener = None,
|
||||||
log_file = None):
|
log_file = None):
|
||||||
|
|
||||||
# Instantiate our low-level transmit interface, be it a serial port, or the BinaryDebug class.
|
self.radio = radio
|
||||||
if debug == True:
|
|
||||||
self.s = BinaryDebug()
|
|
||||||
self.debug = True
|
|
||||||
else:
|
|
||||||
self.debug = False
|
|
||||||
self.s = serial.Serial(serial_port,serial_baud)
|
|
||||||
|
|
||||||
|
|
||||||
self.payload_length = payload_length
|
self.payload_length = payload_length
|
||||||
self.callsign = callsign.encode('ascii')
|
self.callsign = callsign.encode('ascii')
|
||||||
|
@ -159,19 +153,16 @@ class PacketTX(object):
|
||||||
while self.transmit_active:
|
while self.transmit_active:
|
||||||
if self.telemetry_queue.qsize()>0:
|
if self.telemetry_queue.qsize()>0:
|
||||||
packet = self.telemetry_queue.get_nowait()
|
packet = self.telemetry_queue.get_nowait()
|
||||||
self.s.write(packet)
|
self.radio.transmit_packet(packet)
|
||||||
elif self.ssdv_queue.qsize()>0:
|
elif self.ssdv_queue.qsize()>0:
|
||||||
packet = self.ssdv_queue.get_nowait()
|
packet = self.ssdv_queue.get_nowait()
|
||||||
self.s.write(packet)
|
self.radio.transmit_packet(packet)
|
||||||
else:
|
else:
|
||||||
if not self.debug:
|
self.radio.transmit_packet(self.idle_message)
|
||||||
self.s.write(self.idle_message)
|
time.sleep(0.1)
|
||||||
else:
|
|
||||||
# TODO: Tune this value.
|
|
||||||
sleep(0.05)
|
|
||||||
|
|
||||||
print("Closing Thread")
|
print("Closing Thread")
|
||||||
self.s.close()
|
self.radio.shutdown()
|
||||||
|
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
|
@ -534,16 +525,39 @@ if __name__ == "__main__":
|
||||||
import argparse
|
import argparse
|
||||||
|
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument("--txport", default="/dev/ttyAMA0", type=str, help="Transmitter serial port. Defaults to /dev/ttyAMA0")
|
parser.add_argument("--rfm98w", default=None, type=int, help="If set, configure a RFM98W on this SPI device number.")
|
||||||
parser.add_argument("--baudrate", default=115200, type=int, help="Transmitter baud rate. Defaults to 115200 baud.")
|
parser.add_argument("--frequency", default=443.500, type=float, help="Transmit Frequency (MHz). (Default: 443.500 MHz)")
|
||||||
|
parser.add_argument("--baudrate", default=115200, type=int, help="Wenet TX baud rate. (Default: 115200).")
|
||||||
|
parser.add_argument("--serial_port", default="/dev/ttyAMA0", type=str, help="Serial Port for modulation.")
|
||||||
|
parser.add_argument("--tx_power", default=17, type=int, help="Transmit power in dBm (Default: 17 dBm, 50mW. Allowed values: 2-17)")
|
||||||
|
parser.add_argument("-v", "--verbose", action='store_true', default=False, help="Show additional debug info.")
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
debug_output = False # If True, packet bits are saved to debug.bin as one char per bit.
|
|
||||||
|
|
||||||
|
if args.verbose:
|
||||||
|
logging_level = logging.DEBUG
|
||||||
|
else:
|
||||||
|
logging_level = logging.INFO
|
||||||
|
|
||||||
|
# Set up logging
|
||||||
|
logging.basicConfig(format="%(asctime)s %(levelname)s: %(message)s", level=logging_level)
|
||||||
|
|
||||||
|
radio = None
|
||||||
|
|
||||||
|
if args.rfm98w is not None:
|
||||||
|
radio = RFM98W_Serial(
|
||||||
|
spidevice = args.rfm98w,
|
||||||
|
frequency = args.frequency,
|
||||||
|
baudrate = args.baudrate,
|
||||||
|
serial_port = args.serial_port,
|
||||||
|
tx_power_dbm = args.tx_power
|
||||||
|
)
|
||||||
|
# Other radio options would go here.
|
||||||
|
else:
|
||||||
|
logging.critical("No radio type specified! Exiting")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
tx = PacketTX(
|
tx = PacketTX(
|
||||||
debug=debug_output,
|
radio=radio,
|
||||||
serial_port=args.txport,
|
|
||||||
serial_baud=args.baudrate,
|
|
||||||
udp_listener=55674)
|
udp_listener=55674)
|
||||||
tx.start_tx()
|
tx.start_tx()
|
||||||
|
|
||||||
|
@ -556,4 +570,4 @@ if __name__ == "__main__":
|
||||||
|
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
tx.close()
|
tx.close()
|
||||||
print("Closing")
|
logging.info("Closing")
|
|
@ -72,7 +72,7 @@ def setter(register_address):
|
||||||
|
|
||||||
############################################### Definition of the LoRa class ###########################################
|
############################################### Definition of the LoRa class ###########################################
|
||||||
|
|
||||||
class LoRa(object):
|
class LoRaRFM98W(object):
|
||||||
|
|
||||||
BOARD = None
|
BOARD = None
|
||||||
spi = None
|
spi = None
|
||||||
|
|
|
@ -35,6 +35,17 @@ class WenetPiCamera2(object):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# White balance text to enum lookup
|
||||||
|
wb_lookup = {
|
||||||
|
"auto": controls.AwbModeEnum.Auto,
|
||||||
|
"incandescent": controls.AwbModeEnum.Incandescent,
|
||||||
|
"fluorescent": controls.AwbModeEnum.Fluorescent,
|
||||||
|
"tungsten": controls.AwbModeEnum.Tungsten,
|
||||||
|
"indoor": controls.AwbModeEnum.Indoor,
|
||||||
|
"daylight": controls.AwbModeEnum.Daylight,
|
||||||
|
"cloudy": controls.AwbModeEnum.Cloudy
|
||||||
|
}
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
callsign = "N0CALL",
|
callsign = "N0CALL",
|
||||||
tx_resolution=(1936,1088),
|
tx_resolution=(1936,1088),
|
||||||
|
@ -42,7 +53,7 @@ class WenetPiCamera2(object):
|
||||||
image_delay=0.0,
|
image_delay=0.0,
|
||||||
vertical_flip = False,
|
vertical_flip = False,
|
||||||
horizontal_flip = False,
|
horizontal_flip = False,
|
||||||
greyworld = False,
|
whitebalance = 'auto',
|
||||||
lens_position = 0.0,
|
lens_position = 0.0,
|
||||||
temp_filename_prefix = 'picam_temp',
|
temp_filename_prefix = 'picam_temp',
|
||||||
debug_ptr = None
|
debug_ptr = None
|
||||||
|
@ -64,7 +75,7 @@ class WenetPiCamera2(object):
|
||||||
vertical_flip: Flip captured images vertically.
|
vertical_flip: Flip captured images vertically.
|
||||||
horizontal_flip: Flip captured images horizontally.
|
horizontal_flip: Flip captured images horizontally.
|
||||||
Used to correct for picam orientation.
|
Used to correct for picam orientation.
|
||||||
greyworld: Use Greyworld AWB setting, for IR-filtered images.
|
whitebalance: White balance mode - allowed values: Auto, Incandescent, Tungesten, Fluorescent, Indoor, Daylight, Cloudy
|
||||||
lens_position: Lens Position setting (float), 0.0 = Infinity, 10 = very close.
|
lens_position: Lens Position setting (float), 0.0 = Infinity, 10 = very close.
|
||||||
Only usable on Pi Camera v3 modules.
|
Only usable on Pi Camera v3 modules.
|
||||||
|
|
||||||
|
@ -81,12 +92,17 @@ class WenetPiCamera2(object):
|
||||||
self.num_images = num_images
|
self.num_images = num_images
|
||||||
self.image_delay = image_delay
|
self.image_delay = image_delay
|
||||||
self.callsign = callsign
|
self.callsign = callsign
|
||||||
self.tx_resolution = tx_resolution
|
self.tx_resolution_init = tx_resolution
|
||||||
self.horizontal_flip = horizontal_flip
|
self.horizontal_flip = horizontal_flip
|
||||||
self.vertical_flip = vertical_flip
|
self.vertical_flip = vertical_flip
|
||||||
self.greyworld = greyworld
|
|
||||||
self.lens_position = lens_position
|
self.lens_position = lens_position
|
||||||
|
|
||||||
|
if whitebalance.lower() in self.wb_lookup:
|
||||||
|
self.whitebalance = self.wb_lookup[whitebalance.lower()]
|
||||||
|
else:
|
||||||
|
self.whitebalance = self.wb_lookup['auto']
|
||||||
|
|
||||||
|
|
||||||
self.init_camera()
|
self.init_camera()
|
||||||
|
|
||||||
|
|
||||||
|
@ -96,7 +112,18 @@ class WenetPiCamera2(object):
|
||||||
|
|
||||||
self.camera_properties = self.cam.camera_properties
|
self.camera_properties = self.cam.camera_properties
|
||||||
|
|
||||||
self.debug_ptr("Camera Resolution: " + str(self.camera_properties['PixelArraySize']))
|
self.debug_ptr("Camera Native Resolution: " + str(self.camera_properties['PixelArraySize']))
|
||||||
|
|
||||||
|
# If the user has explicitly specified the transmit image resolution, use it.
|
||||||
|
if type(self.tx_resolution_init) == tuple:
|
||||||
|
self.tx_resolution = self.tx_resolution_init
|
||||||
|
self.debug_ptr(f"Transmit Resolution set to {str(self.tx_resolution)}")
|
||||||
|
# Otherwise, has the user provided a floating point scaling factor?
|
||||||
|
elif type(self.tx_resolution_init) == float:
|
||||||
|
res_x = 16*int(self.camera_properties['PixelArraySize'][0]*self.tx_resolution_init/16)
|
||||||
|
res_y = 16*int(self.camera_properties['PixelArraySize'][1]*self.tx_resolution_init/16)
|
||||||
|
self.tx_resolution = (res_x, res_y)
|
||||||
|
self.debug_ptr(f"Transmit Resolution set to {str(self.tx_resolution)}, scaled {self.tx_resolution_init} from native.")
|
||||||
|
|
||||||
# Configure camera, including flip settings.
|
# Configure camera, including flip settings.
|
||||||
capture_config = self.cam.create_still_configuration(
|
capture_config = self.cam.create_still_configuration(
|
||||||
|
@ -105,14 +132,15 @@ class WenetPiCamera2(object):
|
||||||
self.cam.configure(capture_config)
|
self.cam.configure(capture_config)
|
||||||
|
|
||||||
# Set other settings, White Balance, exposure metering, etc.
|
# Set other settings, White Balance, exposure metering, etc.
|
||||||
|
|
||||||
self.cam.set_controls(
|
self.cam.set_controls(
|
||||||
{'AwbMode': controls.AwbModeEnum.Daylight,
|
{'AwbMode': self.whitebalance,
|
||||||
'AeMeteringMode': controls.AeMeteringModeEnum.Matrix,
|
'AeMeteringMode': controls.AeMeteringModeEnum.Matrix,
|
||||||
'NoiseReductionMode': controls.draft.NoiseReductionModeEnum.Off}
|
'NoiseReductionMode': controls.draft.NoiseReductionModeEnum.Off}
|
||||||
)
|
)
|
||||||
|
|
||||||
# Set Pi Camera 3 lens position
|
# Set Pi Camera 3 lens position
|
||||||
if 'LensPosition' in self.cam.camera_controls:
|
if 'LensPosition' in self.cam.camera_controls and self.lens_position>0:
|
||||||
self.debug_ptr("Configured lens position to " + str(self.lens_position))
|
self.debug_ptr("Configured lens position to " + str(self.lens_position))
|
||||||
self.cam.set_controls({"AfMode": controls.AfModeEnum.Manual, "LensPosition": self.lens_position})
|
self.cam.set_controls({"AfMode": controls.AfModeEnum.Manual, "LensPosition": self.lens_position})
|
||||||
|
|
||||||
|
@ -153,13 +181,13 @@ class WenetPiCamera2(object):
|
||||||
|
|
||||||
# Set other settings, White Balance, exposure metering, etc.
|
# Set other settings, White Balance, exposure metering, etc.
|
||||||
self.cam.set_controls(
|
self.cam.set_controls(
|
||||||
{'AwbMode': controls.AwbModeEnum.Daylight,
|
{'AwbMode': self.whitebalance,
|
||||||
'AeMeteringMode': controls.AeMeteringModeEnum.Matrix,
|
'AeMeteringMode': controls.AeMeteringModeEnum.Matrix,
|
||||||
'NoiseReductionMode': controls.draft.NoiseReductionModeEnum.Off}
|
'NoiseReductionMode': controls.draft.NoiseReductionModeEnum.Off}
|
||||||
)
|
)
|
||||||
|
|
||||||
# Set Pi Camera 3 lens position
|
# Set Pi Camera 3 lens position
|
||||||
if 'LensPosition' in self.cam.camera_controls:
|
if 'LensPosition' in self.cam.camera_controls and self.lens_position>0:
|
||||||
self.debug_ptr("Configured lens position to " + str(self.lens_position))
|
self.debug_ptr("Configured lens position to " + str(self.lens_position))
|
||||||
self.cam.set_controls({"AfMode": controls.AfModeEnum.Manual, "LensPosition": self.lens_position})
|
self.cam.set_controls({"AfMode": controls.AfModeEnum.Manual, "LensPosition": self.lens_position})
|
||||||
|
|
||||||
|
|
|
@ -1,279 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
#
|
|
||||||
# RFM22B Initialisation for Horus High Speed Telemetry
|
|
||||||
#
|
|
||||||
# Copyright (C) 2018 Mark Jessop <vk5qi@rfhead.net>
|
|
||||||
# Released under GNU GPL v3 or later
|
|
||||||
#
|
|
||||||
# A mash together of a few different libraries, including:
|
|
||||||
# https://github.com/MaxBaex/raspi_rfm22
|
|
||||||
# https://github.com/omcaree/rfm22b/blob/master/src/rfm22b.cpp
|
|
||||||
#
|
|
||||||
# Uses spidev for SPI comms.
|
|
||||||
#
|
|
||||||
# Note: This is fairly limited, and was only intended to get a RFM22B
|
|
||||||
# into direct-async TX mode.
|
|
||||||
# Lots of functions (i.e. anything to do with packets) are unimplemented.
|
|
||||||
#
|
|
||||||
# Electrical Connections:
|
|
||||||
# GPIO0 <-> TX_ANT
|
|
||||||
# GPIO1 <-> RX_ANT
|
|
||||||
# SDN <-> GND (Not sure if this is a good thing, not being about to hard reset it..)
|
|
||||||
# GPIO2 <-> UART TXD (for direct mode TX)
|
|
||||||
# SPI - connected to CE1 (can change this when instantiating the object.)
|
|
||||||
#
|
|
||||||
|
|
||||||
import spidev, sys, time
|
|
||||||
|
|
||||||
class REG:
|
|
||||||
DEVICE_TYPE = 0x00
|
|
||||||
DEVICE_VERSION = 0x01
|
|
||||||
DEVICE_STATUS = 0x02
|
|
||||||
INTERRUPT_STATUS_1 = 0x03
|
|
||||||
INTERRUPT_STATUS_2 = 0x04
|
|
||||||
INTERRUPT_ENABLE_1 = 0x05
|
|
||||||
INTERRUPT_ENABLE_2 = 0x06
|
|
||||||
OPERATING_FUNCTION_CONTROL_1 = 0x07
|
|
||||||
OPERATING_FUNCTION_CONTROL_2 = 0x08
|
|
||||||
CRYSTAL_OSCILLATOR_LOAD = 0x09
|
|
||||||
MICROCONTROLLER_CLOCK_OUTPUT = 0x0A
|
|
||||||
GPIO0_CONFIGURATION = 0x0B
|
|
||||||
GPIO1_CONFIGURATION = 0x0C
|
|
||||||
GPIO2_CONFIGURATION = 0x0D
|
|
||||||
I_O_PORT_CONFIGURATION = 0x0E
|
|
||||||
ADC_CONFIGURATION = 0x0F
|
|
||||||
ADC_SENSOR_AMPLIFIER_OFFSET = 0x10
|
|
||||||
ADC_VALUE = 0x11
|
|
||||||
TEMPERATURE_SENSOR_CONTROL = 0x12
|
|
||||||
TEMPERATURE_VALUE_OFFSET = 0x13
|
|
||||||
WAKE_UP_TIMER_PERIOD_1 = 0x14
|
|
||||||
WAKE_UP_TIMER_PERIOD_2 = 0x15
|
|
||||||
WAKE_UP_TIMER_PERIOD_3 = 0x16
|
|
||||||
WAKE_UP_TIMER_VALUE_1 = 0x17
|
|
||||||
WAKE_UP_TIMER_VALUE_2 = 0x18
|
|
||||||
LOW_DUTY_CYCLE_MODE_DURATION = 0x19
|
|
||||||
LOW_BATTERY_DETECTION_THRESHOLD = 0x1A
|
|
||||||
BATTERY_VOLTAGE_LEVEL = 0x1B
|
|
||||||
IF_FILTER_BANDWIDTH = 0x1C
|
|
||||||
AFC_LOOP_GEARSHIFT_OVERRIDE = 0x1D
|
|
||||||
AFC_TIMING_CONTROL = 0x1E
|
|
||||||
CLOCK_RECOVERY_GEARSHIFT_OVERRIDE = 0x1F
|
|
||||||
CLOCK_RECOVERY_OVERSAMPLING_RATIO = 0x20
|
|
||||||
CLOCK_RECOVERY_OFFSET_2 = 0x21
|
|
||||||
CLOCK_RECOVERY_OFFSET_1 = 0x22
|
|
||||||
CLOCK_RECOVERY_OFFSET_0 = 0x23
|
|
||||||
CLOCK_RECOVERY_TIMING_LOOP_GAIN_1 = 0x24
|
|
||||||
CLOCK_RECOVERY_TIMING_LOOP_GAIN_0 = 0x25
|
|
||||||
RECEIVED_SIGNAL_STRENGTH_INDICATOR = 0x26
|
|
||||||
RSSI_THRESSHOLF_FOR_CLEAR_CHANNEL_INDICATOR = 0x27
|
|
||||||
ANTENNA_DIVERSITY_REGISTER_1 = 0x28
|
|
||||||
ANTENNA_DIVERSITY_REGISTER_2 = 0x29
|
|
||||||
AFC_LIMITER = 0x2A
|
|
||||||
AFC_CORRECTION_READ = 0x2B
|
|
||||||
OOK_COUNTER_VALUE_1 = 0x2C
|
|
||||||
OOK_COUNTER_VALUE_2 = 0x2D
|
|
||||||
SLICER_PEAK_HOLD = 0x2E
|
|
||||||
# Register 0x2F reserved
|
|
||||||
DATA_ACCESS_CONTROL = 0x30
|
|
||||||
EzMAC_STATUS = 0x31
|
|
||||||
HEADER_CONTROL_1 = 0x32
|
|
||||||
HEADER_CONTROL_2 = 0x33
|
|
||||||
PREAMBLE_LENGTH = 0x34
|
|
||||||
PREAMBLE_DETECTION_CONTROL = 0x35
|
|
||||||
SYNC_WORD_3 = 0x36
|
|
||||||
SYNC_WORD_2 = 0x37
|
|
||||||
SYNC_WORD_1 = 0x38
|
|
||||||
SYNC_WORD_0 = 0x39
|
|
||||||
TRANSMIT_HEADER_3 = 0x3A
|
|
||||||
TRANSMIT_HEADER_2 = 0x3B
|
|
||||||
TRANSMIT_HEADER_1 = 0x3C
|
|
||||||
TRANSMIT_HEADER_0 = 0x3D
|
|
||||||
TRANSMIT_PACKET_LENGTH = 0x3E
|
|
||||||
CHECK_HEADER_3 = 0x3F
|
|
||||||
CHECK_HEADER_2 = 0x40
|
|
||||||
CHECK_HEADER_1 = 0x41
|
|
||||||
CHECK_HEADER_0 = 0x42
|
|
||||||
HEADER_ENABLE_3 = 0x43
|
|
||||||
HEADER_ENABLE_2 = 0x44
|
|
||||||
HEADER_ENABLE_1 = 0x45
|
|
||||||
HEADER_ENABLE_0 = 0x46
|
|
||||||
RECEIVED_HEADER_3 = 0x47
|
|
||||||
RECEIVED_HEADER_2 = 0x48
|
|
||||||
RECEIVED_HEADER_1 = 0x49
|
|
||||||
RECEIVED_HEADER_0 = 0x4A
|
|
||||||
RECEIVED_PACKET_LENGTH = 0x4B
|
|
||||||
# Registers 0x4C-4E reserved
|
|
||||||
ADC8_CONTROL = 0x4F
|
|
||||||
# Registers 0x50-5F reserved
|
|
||||||
CHANNEL_FILTER_COEFFICIENT_ADDRESS = 0x60
|
|
||||||
# Register 0x61 reserved
|
|
||||||
CRYSTAL_OSCILLATOR_CONTROL_TEST = 0x62
|
|
||||||
# Registers 0x63-68 reserved
|
|
||||||
AGC_OVERRIDE_1 = 0x69
|
|
||||||
# Registers 0x6A-0x6C reserved
|
|
||||||
TX_POWER = 0x6D
|
|
||||||
TX_DATA_RATE_1 = 0x6E
|
|
||||||
TX_DATA_RATE_0 = 0x6F
|
|
||||||
MODULATION_MODE_CONTROL_1 = 0x70
|
|
||||||
MODULATION_MODE_CONTROL_2 = 0x71
|
|
||||||
FREQUENCY_DEVIATION = 0x72
|
|
||||||
FREQUENCY_OFFSET_1 = 0x73
|
|
||||||
FREQUENCY_OFFSET_2 = 0x74
|
|
||||||
FREQUENCY_BAND_SELECT = 0x75
|
|
||||||
NOMINAL_CARRIER_FREQUENCY_1 = 0x76
|
|
||||||
NOMINAL_CARRIER_FREQUENCY_0 = 0x77
|
|
||||||
# Register 0x78 reserved
|
|
||||||
FREQUENCY_HOPPING_CHANNEL_SELECT = 0x79
|
|
||||||
FREQUENCY_HOPPING_STEP_SIZE = 0x7A
|
|
||||||
# Register 0x7B reserved
|
|
||||||
TX_FIFO_CONTROL_1 = 0x7C
|
|
||||||
TX_FIFO_CONTROL_2 = 0x7D
|
|
||||||
RX_FIFO_CONTROL = 0x7E
|
|
||||||
FIFO_ACCESS = 0x7F
|
|
||||||
|
|
||||||
class MODE:
|
|
||||||
IDLE = 0
|
|
||||||
RX = 1
|
|
||||||
TX = 2
|
|
||||||
|
|
||||||
class TXPOW:
|
|
||||||
TXPOW_1DBM = 0x00
|
|
||||||
TXPOW_2DBM = 0x01
|
|
||||||
TXPOW_5DBM = 0x02
|
|
||||||
TXPOW_8DBM = 0x03
|
|
||||||
TXPOW_11DBM = 0x04
|
|
||||||
TXPOW_14DBM = 0x05
|
|
||||||
TXPOW_17DBM = 0x06
|
|
||||||
TXPOW_20DBM = 0x07
|
|
||||||
|
|
||||||
class CONFIGS:
|
|
||||||
# Register: 1c, 1f, 20, 21, 22, 23, 24, 25, 2c, 2d, 2e, 58, 69, 6e, 6f, 70, 71, 72
|
|
||||||
REGISTERS = [0x1C,0x1F,0x20,0x21,0x22,0x23,0x24,0x25,0x2C,0x2D,0x2E,0x58,0x69,0x6E,0x6F,0x70,0x71,0x72]
|
|
||||||
DIRECT_120K = [0x8a, 0x03, 0x60, 0x01, 0x55, 0x55, 0x02, 0xad, 0x40, 0x0a, 0x50, 0x80, 0x60, 0x20, 0x00, 0x00, 0x02, 0x60]
|
|
||||||
|
|
||||||
class RFM22B(object):
|
|
||||||
|
|
||||||
def __init__(self,device=1):
|
|
||||||
self.spi = spidev.SpiDev()
|
|
||||||
self.spi.open(0,device)
|
|
||||||
self.spi.max_speed_hz = 1000000
|
|
||||||
|
|
||||||
if not self.check_connection():
|
|
||||||
print("Init Failed!")
|
|
||||||
self.spi.close()
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
self.setup_basic()
|
|
||||||
|
|
||||||
print("Init Complete!")
|
|
||||||
|
|
||||||
def close(self):
|
|
||||||
self.spi.close()
|
|
||||||
|
|
||||||
def read_register(self,address):
|
|
||||||
return self.spi.xfer([address,0])[1]
|
|
||||||
|
|
||||||
def write_register(self,address,data):
|
|
||||||
return self.spi.xfer([address | 0x80, data])
|
|
||||||
|
|
||||||
def check_connection(self):
|
|
||||||
device_type = self.read_register(REG.DEVICE_TYPE)
|
|
||||||
device_version = self.read_register(REG.DEVICE_VERSION)
|
|
||||||
|
|
||||||
print("Device Type:\t %d" % device_type)
|
|
||||||
print("Device Version:\t %d" % device_version)
|
|
||||||
|
|
||||||
if (device_type != 8) or (device_version != 6):
|
|
||||||
print("Wrong Device?!")
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
return True
|
|
||||||
# 441.2E6 = HBSEL: 0 FB: 20 FBS: 84 FC: 7679 NCF1: 29, NCF0: 255
|
|
||||||
def set_frequency(self, freq_hz):
|
|
||||||
if (freq_hz<240E6) or (freq_hz>960E6):
|
|
||||||
print("Invalid Frequency!")
|
|
||||||
return False
|
|
||||||
|
|
||||||
hbsel = 1 if (freq_hz>= 480E6) else 0
|
|
||||||
|
|
||||||
fb = int(freq_hz/10E6/(hbsel+1) - 24)
|
|
||||||
fbs = (1<<6) | (hbsel<<5) | fb
|
|
||||||
fc = int((freq_hz/(10.0E6 * (hbsel+1)) - fb - 24) * 64000)
|
|
||||||
|
|
||||||
ncf1 = fc>>8
|
|
||||||
ncf0 = fc & 0xFF
|
|
||||||
|
|
||||||
print("hbsel: %d, fb: %d, fbs: %d, fc: %d, ncf1: %d, ncf0: %d" % (hbsel,fb,fbs,fc,ncf1,ncf0))
|
|
||||||
|
|
||||||
self.write_register(REG.FREQUENCY_BAND_SELECT,fbs)
|
|
||||||
self.write_register(REG.NOMINAL_CARRIER_FREQUENCY_1,ncf1)
|
|
||||||
self.write_register(REG.NOMINAL_CARRIER_FREQUENCY_0,ncf0)
|
|
||||||
|
|
||||||
# Read back registers.
|
|
||||||
fbs_read = self.read_register(REG.FREQUENCY_BAND_SELECT)
|
|
||||||
ncf1_read = self.read_register(REG.NOMINAL_CARRIER_FREQUENCY_1)
|
|
||||||
ncf0_read = self.read_register(REG.NOMINAL_CARRIER_FREQUENCY_0)
|
|
||||||
|
|
||||||
if (fbs == fbs_read) and (ncf1 == ncf1_read) and (ncf0 == ncf0_read):
|
|
||||||
print("Frequency set OK!")
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
print("Frequency not set correctly!")
|
|
||||||
return False
|
|
||||||
|
|
||||||
def setup_basic(self):
|
|
||||||
# Software reset.
|
|
||||||
self.write_register(REG.OPERATING_FUNCTION_CONTROL_1,0x80)
|
|
||||||
time.sleep(0.1)
|
|
||||||
# Interrupts
|
|
||||||
self.write_register(REG.INTERRUPT_ENABLE_1,0x87)
|
|
||||||
# Switch to ready mode.
|
|
||||||
self.write_register(REG.OPERATING_FUNCTION_CONTROL_1,0x01)
|
|
||||||
self.write_register(REG.CRYSTAL_OSCILLATOR_LOAD,0x7f)
|
|
||||||
# Configure GPIO 0 and 1 settings.
|
|
||||||
self.write_register(REG.GPIO0_CONFIGURATION, 0x12) # TX State, physically wired into TX_ANT pin.
|
|
||||||
self.write_register(REG.GPIO1_CONFIGURATION, 0x15) # RX State, physically wired into RX_ANT pin.
|
|
||||||
|
|
||||||
# Program a bulk set of registers.
|
|
||||||
# I'm using this to set a bunch of different registers at one time, to load up pre-calculated configs.
|
|
||||||
def set_bulk(self,registers,data):
|
|
||||||
if len(registers) != len(data):
|
|
||||||
print("Array length mismatch!")
|
|
||||||
return False
|
|
||||||
|
|
||||||
for k in xrange(len(registers)):
|
|
||||||
self.write_register(registers[k],data[k])
|
|
||||||
|
|
||||||
def set_tx_power(self,power):
|
|
||||||
self.write_register(REG.TX_POWER,power)
|
|
||||||
|
|
||||||
def set_mode(self,mode):
|
|
||||||
self.write_register(REG.OPERATING_FUNCTION_CONTROL_1,mode)
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
|
|
||||||
# Attempt to extract a frequency from the first argument:
|
|
||||||
if len(sys.argv)<2:
|
|
||||||
print("Usage: \tpython init_rfm22b.py <TX Frequency in MHz>")
|
|
||||||
print("Example: \tpython init_rfm22b.py 441.200")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
try:
|
|
||||||
tx_freq = float(sys.argv[1])
|
|
||||||
except:
|
|
||||||
print("Unable to parse input.")
|
|
||||||
|
|
||||||
if tx_freq>450.0 or tx_freq<430.00:
|
|
||||||
print("Frequency out of 70cm band, using default.")
|
|
||||||
tx_freq = 441.200*1e6
|
|
||||||
else:
|
|
||||||
tx_freq = tx_freq*1e6
|
|
||||||
|
|
||||||
rfm = RFM22B()
|
|
||||||
rfm.set_tx_power(TXPOW.TXPOW_17DBM | 0x08)
|
|
||||||
rfm.set_frequency(tx_freq)
|
|
||||||
rfm.write_register(REG.GPIO2_CONFIGURATION,0x30) # TX Data In
|
|
||||||
rfm.set_bulk(CONFIGS.REGISTERS,CONFIGS.DIRECT_120K) # Direct Asynchronous mode, ~120KHz tone spacing.
|
|
||||||
rfm.set_mode(0x09)
|
|
||||||
rfm.close()
|
|
||||||
|
|
|
@ -1,91 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
#
|
|
||||||
# RFM98W Initialisation for Wenet Telemetry
|
|
||||||
#
|
|
||||||
# Copyright (C) 2018 Mark Jessop <vk5qi@rfhead.net>
|
|
||||||
# Released under GNU GPL v3 or later
|
|
||||||
#
|
|
||||||
# Requires pySX127x:
|
|
||||||
# https://github.com/darksidelemm/pySX127x
|
|
||||||
# Copy the SX127x directory into this directory...
|
|
||||||
#
|
|
||||||
# Uses spidev for comms with a RFM98W module.
|
|
||||||
#
|
|
||||||
# Note: As with the RFM22B version, all this script does
|
|
||||||
# is get the RFM98W onto the right frequency, and into the right mode.
|
|
||||||
#
|
|
||||||
# SPI: Connected to CE0 (like most of my LoRa shields)
|
|
||||||
# RPi TXD: Connected to RFM98W's DIO2 pin.
|
|
||||||
#
|
|
||||||
import sys
|
|
||||||
import argparse
|
|
||||||
from SX127x.LoRa import *
|
|
||||||
from SX127x.hardware_piloragateway import HardwareInterface
|
|
||||||
|
|
||||||
def setup_rfm98w(frequency=441.200, spi_device=0, shutdown=False, deviation = 71797):
|
|
||||||
# Set this to 1 if your RFM98W is on CE1
|
|
||||||
hw = HardwareInterface(spi_device)
|
|
||||||
|
|
||||||
# Start talking to the module...
|
|
||||||
lora = LoRa(hw)
|
|
||||||
|
|
||||||
# Set us into FSK mode, and set continuous mode on
|
|
||||||
lora.set_register(0x01,0x00) # Standby Mode
|
|
||||||
# If we have been asked to shutdown the RFM98W, then exit here.
|
|
||||||
if shutdown:
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
# Otherwise, proceed.
|
|
||||||
lora.set_register(0x31,0x00) # Set Continuous Mode
|
|
||||||
|
|
||||||
# Set TX Frequency
|
|
||||||
lora.set_freq(frequency)
|
|
||||||
|
|
||||||
# Set Deviation (~70 kHz). Signals ends up looking a bit wider than the RFM22B version.
|
|
||||||
_dev_lsbs = int(deviation / 61.03)
|
|
||||||
_dev_msb = _dev_lsbs >> 8
|
|
||||||
_dev_lsb = _dev_lsbs % 256
|
|
||||||
|
|
||||||
lora.set_register(0x04,_dev_msb)
|
|
||||||
lora.set_register(0x05,_dev_lsb)
|
|
||||||
|
|
||||||
# Set Transmit power to 50mW.
|
|
||||||
# NOTE: If you're in another country you'll probably want to modify this value to something legal...
|
|
||||||
lora.set_register(0x09,0x8F)
|
|
||||||
|
|
||||||
# Go into TX mode.
|
|
||||||
lora.set_register(0x01,0x02) # .. via FSTX mode (where the transmit frequency actually gets set)
|
|
||||||
lora.set_register(0x01,0x03) # Now we're in TX mode...
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser()
|
|
||||||
parser.add_argument("--frequency", default=441.200, type=float, help="Transmit Frequency (MHz). Default = 441.200 MHz.")
|
|
||||||
parser.add_argument("--spidevice", default=0, type=int, help="LoRa SPI Device number. Default = 0.")
|
|
||||||
parser.add_argument("--baudrate", default=115200, type=int, help="Wenet TX baud rate. We chose a TX deviation based on this. Default=115200.")
|
|
||||||
parser.add_argument("--shutdown",action="store_true", help="Shutdown Transmitter instead of activating it.")
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
tx_freq = args.frequency
|
|
||||||
|
|
||||||
if tx_freq>450.0 or tx_freq<430.00:
|
|
||||||
print("Frequency out of 70cm band, using default.")
|
|
||||||
tx_freq = 441.200
|
|
||||||
|
|
||||||
|
|
||||||
if args.baudrate == 9600:
|
|
||||||
deviation = 4800
|
|
||||||
elif args.baudrate == 4800:
|
|
||||||
deviation = 2400
|
|
||||||
else:
|
|
||||||
deviation = 71797
|
|
||||||
|
|
||||||
print("Using deviation of %d Hz for Baud Rate %d bd." % (deviation, args.baudrate))
|
|
||||||
|
|
||||||
setup_rfm98w(frequency=tx_freq, spi_device=args.spidevice, shutdown=args.shutdown, deviation=deviation)
|
|
||||||
|
|
||||||
print("Frequency set to: %.3f MHz" % tx_freq)
|
|
||||||
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,285 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
#
|
||||||
|
# Radio Wrappers for Wenet Transmissions
|
||||||
|
#
|
||||||
|
# Copyright (C) 2024 Mark Jessop <vk5qi@rfhead.net>
|
||||||
|
# Released under GNU GPL v3 or later
|
||||||
|
#
|
||||||
|
# For RFM98W support, requires pySX127x:
|
||||||
|
# https://github.com/darksidelemm/pySX127x
|
||||||
|
# (This is included with the Wenet repository)
|
||||||
|
#
|
||||||
|
# Uses spidev for comms with a RFM98W module.
|
||||||
|
#
|
||||||
|
# Note: As with the RFM22B version, all this script does
|
||||||
|
# is get the RFM98W onto the right frequency, and into the right mode.
|
||||||
|
#
|
||||||
|
# SPI: Connected to CE0 (like most of my LoRa shields)
|
||||||
|
# RPi TXD: Connected to RFM98W's DIO2 pin.
|
||||||
|
#
|
||||||
|
import sys
|
||||||
|
import argparse
|
||||||
|
import logging
|
||||||
|
import serial
|
||||||
|
import time
|
||||||
|
import numpy as np
|
||||||
|
from SX127x.LoRa import *
|
||||||
|
from SX127x.hardware_piloragateway import HardwareInterface
|
||||||
|
|
||||||
|
|
||||||
|
class RFM98W_Serial(object):
|
||||||
|
"""
|
||||||
|
RFM98W Wrapper for Wenet Transmission, using 2-FSK Direct-Asynchronous Modulation via a UART.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
spidevice=0,
|
||||||
|
frequency=443.500,
|
||||||
|
baudrate=115200,
|
||||||
|
serial_port=None,
|
||||||
|
tx_power_dbm=17,
|
||||||
|
reinit_count=5000
|
||||||
|
):
|
||||||
|
|
||||||
|
self.spidevice = spidevice
|
||||||
|
self.frequency = frequency
|
||||||
|
self.baudrate = baudrate
|
||||||
|
self.serial_port = serial_port
|
||||||
|
self.tx_power_dbm = tx_power_dbm
|
||||||
|
self.reinit_count = reinit_count
|
||||||
|
|
||||||
|
self.hw = None
|
||||||
|
self.lora = None
|
||||||
|
|
||||||
|
self.tx_packet_count = 0
|
||||||
|
|
||||||
|
self.start()
|
||||||
|
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
"""
|
||||||
|
Initialise (or re-initialise) both the RFM98W and Serial connections.
|
||||||
|
Configure the RFM98W into direct asynchronous FSK mode, with the appropriate power, deviation, and transmit frequency.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.hw = HardwareInterface(self.spidevice)
|
||||||
|
self.lora = LoRaRFM98W(self.hw, verbose=False)
|
||||||
|
|
||||||
|
logging.debug(f"RFM98W - SX127x Register Dump: {self.lora.backup_registers}")
|
||||||
|
logging.debug(f"RFM98W - SX127x device version: {hex(self.lora.get_version())}")
|
||||||
|
|
||||||
|
if not self.comms_ok():
|
||||||
|
logging.critical("RFM98W - No communication with RFM98W IC!")
|
||||||
|
self.shutdown()
|
||||||
|
return
|
||||||
|
|
||||||
|
# Deviation selection.
|
||||||
|
if self.baudrate == 9600:
|
||||||
|
deviation = 4800
|
||||||
|
elif self.baudrate == 4800:
|
||||||
|
deviation = 2400
|
||||||
|
else:
|
||||||
|
# Default deviation, for 115200 baud
|
||||||
|
deviation = 71797
|
||||||
|
|
||||||
|
# Refer https://cdn.sparkfun.com/assets/learn_tutorials/8/0/4/RFM95_96_97_98W.pdf
|
||||||
|
self.lora.set_register(0x01,0x00) # FSK Sleep Mode
|
||||||
|
self.lora.set_register(0x31,0x00) # Set Continuous Transmit Mode
|
||||||
|
|
||||||
|
self.lora.set_freq(self.frequency)
|
||||||
|
logging.info(f"RFM98W - Frequency set to: {self.frequency} MHz.")
|
||||||
|
|
||||||
|
# Set Deviation (~70 kHz). Signals ends up looking a bit wider than the RFM22B version.
|
||||||
|
_dev_lsbs = int(deviation / 61.03)
|
||||||
|
_dev_msb = _dev_lsbs >> 8
|
||||||
|
_dev_lsb = _dev_lsbs % 256
|
||||||
|
self.lora.set_register(0x04,_dev_msb)
|
||||||
|
self.lora.set_register(0x05,_dev_lsb)
|
||||||
|
|
||||||
|
# Set Transmit power
|
||||||
|
tx_power_lookup = {0:0x80, 1:0x80, 2:0x80, 3:0x81, 4:0x82, 5:0x83, 6:0x84, 7:0x85, 8:0x86, 9:0x87, 10:0x88, 11:0x89, 12:0x8A, 13:0x8B, 14:0x8C, 15:0x8D, 16:0x8E, 17:0x8F}
|
||||||
|
if self.tx_power_dbm in tx_power_lookup:
|
||||||
|
self.lora.set_register(0x09, tx_power_lookup[self.tx_power_dbm])
|
||||||
|
logging.info(f"RFM98W - TX Power set to {self.tx_power_dbm} dBm ({hex(tx_power_lookup[self.tx_power_dbm])}).")
|
||||||
|
else:
|
||||||
|
# Default to low power, 1.5mW or so
|
||||||
|
self.lora.set_register(0x09, 0x80)
|
||||||
|
logging.info(f"RFM98W - Unknown TX power, setting to 2 dBm (0x80).")
|
||||||
|
|
||||||
|
# Go into TX mode.
|
||||||
|
self.lora.set_register(0x01,0x02) # .. via FSTX mode (where the transmit frequency actually gets set)
|
||||||
|
self.lora.set_register(0x01,0x03) # Now we're in TX mode...
|
||||||
|
|
||||||
|
# Seems we need to briefly sleep before we can read the register correctly.
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
||||||
|
# Confirm we've gone into transmit mode.
|
||||||
|
if self.lora.get_register(0x01) == 0x03:
|
||||||
|
logging.info("RFM98W - Radio initialised!")
|
||||||
|
else:
|
||||||
|
logging.critical("RFM98W - TX Mode not set correctly!")
|
||||||
|
|
||||||
|
# Now initialise the Serial port for modulation
|
||||||
|
if self.serial_port:
|
||||||
|
try:
|
||||||
|
self.serial = serial.Serial(self.serial_port, self.baudrate)
|
||||||
|
logging.info(f"RFM98W - Opened Serial port {self.serial_port} for modulation.")
|
||||||
|
except Exception as e:
|
||||||
|
logging.critical(f"Could not open serial port! Error: {str(e)}")
|
||||||
|
self.serial = None
|
||||||
|
|
||||||
|
else:
|
||||||
|
# If no serial port info provided, write out to a binary debug file.
|
||||||
|
self.serial = BinaryDebug()
|
||||||
|
logging.info("No serial port provided - using Binary Debug output (binary_debug.bin)")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def shutdown(self):
|
||||||
|
"""
|
||||||
|
Shutdown the RFM98W, and close the SPI and Serial connections.
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Set radio into FSK sleep mode
|
||||||
|
self.lora.set_register(0x01,0x00)
|
||||||
|
logging.info("RFM98W - Set radio into sleep mode.")
|
||||||
|
self.lora = None
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Shutdown SPI device
|
||||||
|
self.hw.teardown()
|
||||||
|
logging.info("RFM98W - Disconnected from SPI.")
|
||||||
|
self.hw = None
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Close the serial connection
|
||||||
|
self.serial.close()
|
||||||
|
logging.info("RFM98W - Closed Serial Port")
|
||||||
|
self.serial = None
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def comms_ok(self):
|
||||||
|
"""
|
||||||
|
Test SPI communications with the RFM98W and return true if ok.
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
_ver = self.lora.get_version()
|
||||||
|
|
||||||
|
if _ver == 0x00 or _ver == 0xFF or _ver == None:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
logging.critical("RFM98W - Could not read device version!")
|
||||||
|
return False
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def transmit_packet(self, packet):
|
||||||
|
"""
|
||||||
|
Modulate serial data, using a UART.
|
||||||
|
"""
|
||||||
|
if self.serial:
|
||||||
|
self.serial.write(packet)
|
||||||
|
|
||||||
|
# Increment transmit packet counter
|
||||||
|
self.tx_packet_count += 1
|
||||||
|
|
||||||
|
# If we have a reinitialisation count set, reinitialise the radio.
|
||||||
|
if self.reinit_count:
|
||||||
|
if self.tx_packet_count % self.reinit_count == 0:
|
||||||
|
logging.info(f"RFM98W - Reinitialising Radio at {self.tx_packet_count} packets.")
|
||||||
|
self.start()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class BinaryDebug(object):
|
||||||
|
""" Debug binary 'transmitter' Class
|
||||||
|
Used to write packet data to a file in one-bit-per-char (i.e. 0 = 0x00, 1 = 0x01)
|
||||||
|
format for use with codec2-dev's fsk modulator.
|
||||||
|
Useful for debugging, that's about it.
|
||||||
|
"""
|
||||||
|
def __init__(self):
|
||||||
|
self.f = open("binary_debug.bin",'wb')
|
||||||
|
|
||||||
|
def write(self,data):
|
||||||
|
# TODO: Add in RS232 framing
|
||||||
|
raw_data = np.array([],dtype=np.uint8)
|
||||||
|
for d in data:
|
||||||
|
d_array = np.unpackbits(np.fromstring(d,dtype=np.uint8))
|
||||||
|
raw_data = np.concatenate((raw_data,[0],d_array[::-1],[1]))
|
||||||
|
|
||||||
|
self.f.write(raw_data.astype(np.uint8).tostring())
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
self.f.close()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
|
||||||
|
# Test code for the above. Allows enabling a radio and (optionally) sending some test packets.
|
||||||
|
|
||||||
|
import time
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument("--rfm98w", default=None, type=int, help="If set, configure a RFM98W on this SPI device number.")
|
||||||
|
parser.add_argument("--frequency", default=443.500, type=float, help="Transmit Frequency (MHz). (Default: 443.500 MHz)")
|
||||||
|
parser.add_argument("--baudrate", default=115200, type=int, help="Wenet TX baud rate. (Default: 115200).")
|
||||||
|
parser.add_argument("--serial_port", default="/dev/ttyAMA0", type=str, help="Serial Port for modulation.")
|
||||||
|
parser.add_argument("--tx_power", default=17, type=int, help="Transmit power in dBm (Default: 17 dBm, 50mW. Allowed values: 2-17)")
|
||||||
|
parser.add_argument("--shutdown", default=False, action="store_true", help="Shutdown Transmitter after configuration.")
|
||||||
|
parser.add_argument("--test_modulation", default=False, action="store_true", help="Transmit a sequence of dummy packets as a test.")
|
||||||
|
parser.add_argument("-v", "--verbose", action='store_true', default=False, help="Show additional debug info.")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if args.verbose:
|
||||||
|
logging_level = logging.DEBUG
|
||||||
|
else:
|
||||||
|
logging_level = logging.INFO
|
||||||
|
|
||||||
|
# Set up logging
|
||||||
|
logging.basicConfig(format="%(asctime)s %(levelname)s: %(message)s", level=logging_level)
|
||||||
|
|
||||||
|
radio = None
|
||||||
|
|
||||||
|
if args.rfm98w is not None:
|
||||||
|
radio = RFM98W_Serial(
|
||||||
|
spidevice = args.rfm98w,
|
||||||
|
frequency = args.frequency,
|
||||||
|
baudrate = args.baudrate,
|
||||||
|
serial_port = args.serial_port,
|
||||||
|
tx_power_dbm = args.tx_power
|
||||||
|
)
|
||||||
|
# Other radio options would go here.
|
||||||
|
else:
|
||||||
|
logging.critical("No radio type specified! Exiting")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
if args.test_modulation:
|
||||||
|
# Transmit a canned text message a few times
|
||||||
|
time.sleep(1)
|
||||||
|
test_packet = b'UUUUUUUUUUUUUUUU\xab\xcd\xef\x01\x00\x1d\x00\x01This is a Wenet test message!UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU5\x89\xad5\xff\xfbgX\x96\xaa\x10\xb9\x05,\x8co\xf7\xf0\xdd\x19\x1bs2\xd9$\x85\xa2\xc2\xd5\xc9\x15\xef\xac\x06\xb6\x11H\xb0;\xc3\xae\x1b\xe0_\x8cC\x13L*\x04\x17(\x9a\xa6\x95\x84\xf1UB{\xf5\x96\xb9\x14\x05\xa8@'
|
||||||
|
logging.info("Sending 100 test packets.")
|
||||||
|
for x in range(100):
|
||||||
|
radio.transmit_packet(test_packet)
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
||||||
|
if args.shutdown:
|
||||||
|
logging.info("Sleeping 5 seconds before shutdown")
|
||||||
|
time.sleep(5)
|
||||||
|
radio.shutdown()
|
||||||
|
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
# PiCamera2 Library Transmitter Script - with GPS Data and Logo Overlay.
|
# PiCamera2 Library Transmitter Script - with GPS Data and Logo Overlay.
|
||||||
# Capture images from the PiCam, and transmit them.
|
# Capture images from the PiCam, and transmit them.
|
||||||
#
|
#
|
||||||
# Copyright (C) 2023 Mark Jessop <vk5qi@rfhead.net>
|
# Copyright (C) 2024 Mark Jessop <vk5qi@rfhead.net>
|
||||||
# Released under GNU GPL v3 or later
|
# Released under GNU GPL v3 or later
|
||||||
#
|
#
|
||||||
|
|
||||||
|
@ -11,19 +11,40 @@ import PacketTX
|
||||||
import WenetPiCamera2
|
import WenetPiCamera2
|
||||||
import ublox
|
import ublox
|
||||||
import argparse
|
import argparse
|
||||||
|
import logging
|
||||||
import time
|
import time
|
||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
import traceback
|
import traceback
|
||||||
|
from radio_wrappers import *
|
||||||
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument("callsign", default="N0CALL", help="Payload Callsign")
|
parser.add_argument("callsign", default="N0CALL", help="Payload Callsign")
|
||||||
parser.add_argument("--gps", default="/dev/ttyACM0", help="uBlox GPS Serial port. Defaults to /dev/ttyACM0")
|
parser.add_argument("--gps", default="/dev/ttyACM0", help="uBlox GPS Serial port. Defaults to /dev/ttyACM0")
|
||||||
parser.add_argument("--logo", default="none", help="Optional logo to overlay on image.")
|
parser.add_argument("--logo", default="none", help="Optional logo to overlay on image.")
|
||||||
parser.add_argument("--txport", default="/dev/ttyAMA0", type=str, help="Transmitter serial port. Defaults to /dev/ttyAMA0")
|
parser.add_argument("--rfm98w", default=0, type=int, help="If set, configure a RFM98W on this SPI device number.")
|
||||||
parser.add_argument("--baudrate", default=115200, type=int, help="Transmitter baud rate. Defaults to 115200 baud.")
|
parser.add_argument("--frequency", default=443.500, type=float, help="Transmit Frequency (MHz). (Default: 443.500 MHz)")
|
||||||
|
parser.add_argument("--baudrate", default=115200, type=int, help="Wenet TX baud rate. (Default: 115200).")
|
||||||
|
parser.add_argument("--serial_port", default="/dev/ttyAMA0", type=str, help="Serial Port for modulation.")
|
||||||
|
parser.add_argument("--tx_power", default=17, type=int, help="Transmit power in dBm (Default: 17 dBm, 50mW. Allowed values: 2-17)")
|
||||||
|
parser.add_argument("--vflip", action='store_true', default=False, help="Flip captured image vertically.")
|
||||||
|
parser.add_argument("--hflip", action='store_true', default=False, help="Flip captured image horizontally.")
|
||||||
|
parser.add_argument("--resize", type=float, default=0.5, help="Resize raw image from camera by this factor before transmit (in both X/Y, to nearest multiple of 16 pixels). Default=0.5")
|
||||||
|
parser.add_argument("--whitebalance", type=str, default='daylight', help="White Balance setting: Auto, Daylight, Cloudy, Incandescent, Tungesten, Fluorescent, Indoor")
|
||||||
|
parser.add_argument("--lensposition", type=float, default=-1, help="For PiCam v3, set the lens position. Default: -1 = Autofocus")
|
||||||
|
parser.add_argument("-v", "--verbose", action='store_true', default=False, help="Show additional debug info.")
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if args.verbose:
|
||||||
|
logging_level = logging.DEBUG
|
||||||
|
else:
|
||||||
|
logging_level = logging.INFO
|
||||||
|
|
||||||
|
# Set up logging
|
||||||
|
logging.basicConfig(format="%(asctime)s %(levelname)s: %(message)s", level=logging_level)
|
||||||
|
|
||||||
|
|
||||||
callsign = args.callsign
|
callsign = args.callsign
|
||||||
# Truncate callsign if it's too long.
|
# Truncate callsign if it's too long.
|
||||||
if len(callsign) > 6:
|
if len(callsign) > 6:
|
||||||
|
@ -31,9 +52,22 @@ if len(callsign) > 6:
|
||||||
|
|
||||||
print("Using Callsign: %s" % callsign)
|
print("Using Callsign: %s" % callsign)
|
||||||
|
|
||||||
|
if args.rfm98w is not None:
|
||||||
|
radio = RFM98W_Serial(
|
||||||
|
spidevice = args.rfm98w,
|
||||||
|
frequency = args.frequency,
|
||||||
|
baudrate = args.baudrate,
|
||||||
|
serial_port = args.serial_port,
|
||||||
|
tx_power_dbm = args.tx_power
|
||||||
|
)
|
||||||
|
# Other radio options would go here.
|
||||||
|
else:
|
||||||
|
logging.critical("No radio type specified! Exiting")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
# Start up Wenet TX.
|
# Start up Wenet TX.
|
||||||
tx = PacketTX.PacketTX(serial_port=args.txport, serial_baud=args.baudrate, callsign=callsign, log_file="debug.log", udp_listener=55674)
|
tx = PacketTX.PacketTX(radio=radio, callsign=callsign, log_file="debug.log", udp_listener=55674)
|
||||||
tx.start_tx()
|
tx.start_tx()
|
||||||
|
|
||||||
# Sleep for a second to let the transmitter fire up.
|
# Sleep for a second to let the transmitter fire up.
|
||||||
|
@ -46,11 +80,12 @@ system_time_set = False
|
||||||
# Disable Systemctl NTP synchronization so that we can set the system time on first GPS lock.
|
# Disable Systemctl NTP synchronization so that we can set the system time on first GPS lock.
|
||||||
# This is necessary as NTP will refuse to sync the system time to the information we feed it via ntpshm unless
|
# This is necessary as NTP will refuse to sync the system time to the information we feed it via ntpshm unless
|
||||||
# the system clock is already within a few seconds.
|
# the system clock is already within a few seconds.
|
||||||
ret_code = os.system("timedatectl set-ntp 0")
|
if args.gps.lower() != 'none':
|
||||||
if ret_code == 0:
|
ret_code = os.system("timedatectl set-ntp 0")
|
||||||
tx.transmit_text_message("GPS Debug: Disabled NTP Sync until GPS lock.")
|
if ret_code == 0:
|
||||||
else:
|
tx.transmit_text_message("GPS Debug: Disabled NTP Sync until GPS lock.")
|
||||||
tx.transmit_text_message("GPS Debug: Could not disable NTP sync.")
|
else:
|
||||||
|
tx.transmit_text_message("GPS Debug: Could not disable NTP sync.")
|
||||||
|
|
||||||
def handle_gps_data(gps_data):
|
def handle_gps_data(gps_data):
|
||||||
""" Handle GPS data passed to us from the UBloxGPS instance """
|
""" Handle GPS data passed to us from the UBloxGPS instance """
|
||||||
|
@ -87,14 +122,19 @@ def handle_gps_data(gps_data):
|
||||||
|
|
||||||
|
|
||||||
# Try and start up the GPS rx thread.
|
# Try and start up the GPS rx thread.
|
||||||
|
|
||||||
try:
|
try:
|
||||||
gps = ublox.UBloxGPS(port=args.gps,
|
if args.gps.lower() != 'none':
|
||||||
dynamic_model = ublox.DYNAMIC_MODEL_AIRBORNE1G,
|
gps = ublox.UBloxGPS(port=args.gps,
|
||||||
update_rate_ms = 1000,
|
dynamic_model = ublox.DYNAMIC_MODEL_AIRBORNE1G,
|
||||||
debug_ptr = tx.transmit_text_message,
|
update_rate_ms = 1000,
|
||||||
callback = handle_gps_data,
|
debug_ptr = tx.transmit_text_message,
|
||||||
log_file = 'gps_data.log'
|
callback = handle_gps_data,
|
||||||
)
|
log_file = 'gps_data.log'
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
tx.transmit_text_message("No GPS configured. No GPS data will be overlaid.")
|
||||||
|
gps = None
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
tx.transmit_text_message("ERROR: Could not Open GPS - %s" % str(e), repeats=5)
|
tx.transmit_text_message("ERROR: Could not Open GPS - %s" % str(e), repeats=5)
|
||||||
gps = None
|
gps = None
|
||||||
|
@ -152,12 +192,14 @@ def post_process_image(filename):
|
||||||
|
|
||||||
# Finally, initialise the PiCam capture object.
|
# Finally, initialise the PiCam capture object.
|
||||||
picam = WenetPiCamera2.WenetPiCamera2(
|
picam = WenetPiCamera2.WenetPiCamera2(
|
||||||
tx_resolution=(1936,1088),
|
tx_resolution=args.resize,
|
||||||
callsign=callsign,
|
callsign=callsign,
|
||||||
num_images=5,
|
num_images=5,
|
||||||
debug_ptr=tx.transmit_text_message,
|
debug_ptr=tx.transmit_text_message,
|
||||||
vertical_flip=False,
|
vertical_flip=args.vflip,
|
||||||
horizontal_flip=False)
|
horizontal_flip=args.hflip,
|
||||||
|
whitebalance=args.whitebalance,
|
||||||
|
lens_position=args.lensposition)
|
||||||
# .. and start it capturing continuously.
|
# .. and start it capturing continuously.
|
||||||
picam.run(destination_directory="./tx_images/",
|
picam.run(destination_directory="./tx_images/",
|
||||||
tx = tx,
|
tx = tx,
|
||||||
|
@ -177,7 +219,8 @@ except KeyboardInterrupt:
|
||||||
print("Closing")
|
print("Closing")
|
||||||
picam.stop()
|
picam.stop()
|
||||||
tx.close()
|
tx.close()
|
||||||
gps.close()
|
if gps:
|
||||||
|
gps.close()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,8 @@
|
||||||
# Released under GNU GPL v3 or later
|
# Released under GNU GPL v3 or later
|
||||||
#
|
#
|
||||||
|
|
||||||
import PacketTX, sys, os, argparse
|
import PacketTX, sys, os, argparse, logging
|
||||||
|
from radio_wrappers import *
|
||||||
|
|
||||||
# Set to whatever resolution you want to test.
|
# Set to whatever resolution you want to test.
|
||||||
file_path = "../test_images/%d_raw.bin" # _raw, _800x608, _640x480, _320x240
|
file_path = "../test_images/%d_raw.bin" # _raw, _800x608, _640x480, _320x240
|
||||||
|
@ -35,13 +36,46 @@ def transmit_file(filename, tx_object):
|
||||||
tx_object.wait()
|
tx_object.wait()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument("--baudrate", default=115200, type=int, help="Transmitter baud rate. Defaults to 115200 baud.")
|
parser.add_argument("--rfm98w", default=None, type=int, help="If set, configure a RFM98W on this SPI device number.")
|
||||||
|
parser.add_argument("--frequency", default=443.500, type=float, help="Transmit Frequency (MHz). (Default: 443.500 MHz)")
|
||||||
|
parser.add_argument("--baudrate", default=115200, type=int, help="Wenet TX baud rate. (Default: 115200).")
|
||||||
|
parser.add_argument("--serial_port", default="/dev/ttyAMA0", type=str, help="Serial Port for modulation.")
|
||||||
|
parser.add_argument("--tx_power", default=17, type=int, help="Transmit power in dBm (Default: 17 dBm, 50mW. Allowed values: 2-17)")
|
||||||
|
parser.add_argument("-v", "--verbose", action='store_true', default=False, help="Show additional debug info.")
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if args.verbose:
|
||||||
|
logging_level = logging.DEBUG
|
||||||
|
else:
|
||||||
|
logging_level = logging.INFO
|
||||||
|
|
||||||
|
# Set up logging
|
||||||
|
logging.basicConfig(format="%(asctime)s %(levelname)s: %(message)s", level=logging_level)
|
||||||
|
|
||||||
|
radio = None
|
||||||
|
|
||||||
|
if args.rfm98w is not None:
|
||||||
|
radio = RFM98W_Serial(
|
||||||
|
spidevice = args.rfm98w,
|
||||||
|
frequency = args.frequency,
|
||||||
|
baudrate = args.baudrate,
|
||||||
|
serial_port = args.serial_port,
|
||||||
|
tx_power_dbm = args.tx_power
|
||||||
|
)
|
||||||
|
# Other radio options would go here.
|
||||||
|
else:
|
||||||
|
logging.critical("No radio type specified! Exiting")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
tx = PacketTX.PacketTX(
|
||||||
|
radio=radio,
|
||||||
|
udp_listener=55674)
|
||||||
|
|
||||||
tx = PacketTX.PacketTX(debug=debug_output, serial_baud=args.baudrate)
|
|
||||||
tx.start_tx()
|
tx.start_tx()
|
||||||
|
|
||||||
print("TX Started. Press Ctrl-C to stop.")
|
print("TX Started. Press Ctrl-C to stop.")
|
||||||
try:
|
try:
|
||||||
for img in image_numbers:
|
for img in image_numbers:
|
||||||
|
|
|
@ -3,8 +3,8 @@ Description=wenet_tx
|
||||||
After=basic.target
|
After=basic.target
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
# Update this path if not running as the pi user!
|
# Update this path if the wenet files are not in the pi user's home directory!
|
||||||
ExecStart=/home/pi/wenet/start_tx_systemd.sh
|
ExecStart=/home/pi/wenet/start_tx.sh
|
||||||
Restart=always
|
Restart=always
|
||||||
RestartSec=30
|
RestartSec=30
|
||||||
# This one too!
|
# This one too!
|
||||||
|
|
Ładowanie…
Reference in New Issue