Original files from https://github.com/tomelec/RPi-LoRa-KISS-TNC
parent
b031cb23dd
commit
d7d52900c7
13 changed files with 1768 additions and 2 deletions
@ -0,0 +1,237 @@ |
|||||||
|
#!/usr/bin/python |
||||||
|
# -*- coding: utf-8 -*- |
||||||
|
from __future__ import print_function |
||||||
|
|
||||||
|
RECV_BUFFER_LENGTH = 1024 |
||||||
|
|
||||||
|
from threading import Thread |
||||||
|
import socket |
||||||
|
from KissHelper import SerialParser |
||||||
|
from array import array |
||||||
|
|
||||||
|
|
||||||
|
CRCTAB = array("H",[ |
||||||
|
61560,57841,54122,49891,46684,42965,38222,33991,31792,28089,24354,20139,14868,11165,6406,2191, |
||||||
|
57593,61808,50155,53858,42717,46932,34255,37958,27825,32056,20387,24106,10901,15132,2439,6158, |
||||||
|
53626,49395,62056,58337,38750,34519,46156,42437,23858,19643,32288,28585,6934,2719,14340,10637, |
||||||
|
49659,53362,58089,62304,34783,38486,42189,46404,19891,23610,28321,32552,2967,6686,10373,14604, |
||||||
|
45692,41973,37230,32999,62552,58833,55114,50883,15924,12221,7462,3247,30736,27033,23298,19083, |
||||||
|
41725,45940,33263,36966,58585,62800,51147,54850,11957,16188,3495,7214,26769,31000,19331,23050, |
||||||
|
37758,33527,45164,41445,54618,50387,63048,59329,7990,3775,15396,11693,22802,18587,31232,27529, |
||||||
|
33791,37494,41197,45412,50651,54354,59081,63296,4023,7742,11429,15660,18835,22554,27265,31496, |
||||||
|
29808,26105,22370,18155,12884,9181,4422,207,63544,59825,56106,51875,48668,44949,40206,35975, |
||||||
|
25841,30072,18403,22122,8917,13148,455,4174,59577,63792,52139,55842,44701,48916,36239,39942, |
||||||
|
21874,17659,30304,26601,4950,735,12356,8653,55610,51379,64040,60321,40734,36503,48140,44421, |
||||||
|
17907,21626,26337,30568,983,4702,8389,12620,51643,55346,60073,64288,36767,40470,44173,48388, |
||||||
|
13940,10237,5478,1263,28752,25049,21314,17099,47676,43957,39214,34983,64536,60817,57098,52867, |
||||||
|
9973,14204,1511,5230,24785,29016,17347,21066,43709,47924,35247,38950,60569,64784,53131,56834, |
||||||
|
6006,1791,13412,9709,20818,16603,29248,25545,39742,35511,47148,43429,56602,52371,65032,61313, |
||||||
|
2039,5758,9445,13676,16851,20570,25281,29512,35775,39478,43181,47396,52635,56338,61065,65280 |
||||||
|
]) |
||||||
|
|
||||||
|
def logf(message): |
||||||
|
import sys |
||||||
|
print(message, file=sys.stderr) |
||||||
|
|
||||||
|
|
||||||
|
class AXUDPServer(Thread): |
||||||
|
'''AXUDP Server to communicate with the digipeater''' |
||||||
|
|
||||||
|
txQueue = None |
||||||
|
|
||||||
|
# host and port as configured in aprx/aprx.conf.lora-aprs < interface > section |
||||||
|
def __init__(self, txQueue, localHost="127.0.0.1", localPort=10001, remoteHost="127.0.0.1", remotePort="20000"): |
||||||
|
Thread.__init__(self) |
||||||
|
self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) |
||||||
|
self.socket.bind((localHost, localPort)) |
||||||
|
self.remoteHost=remoteHost |
||||||
|
self.remotePort=remotePort |
||||||
|
self.data = str() |
||||||
|
self.txQueue = txQueue |
||||||
|
|
||||||
|
def run(self): |
||||||
|
while True: |
||||||
|
frame = self.socket.recv(RECV_BUFFER_LENGTH) |
||||||
|
print("TX:",self.axtostr(frame)) |
||||||
|
self.txQueue.put(self.axtostr(frame).encode('utf-8'), block=False) |
||||||
|
|
||||||
|
def __del__(self): |
||||||
|
self.socket.shutdown() |
||||||
|
|
||||||
|
def send(self, data, metadata): |
||||||
|
self.sendax(data, (self.remoteHost, self.remotePort), metadata) |
||||||
|
#self.socket.sendall(data) |
||||||
|
|
||||||
|
def axcall(self, text, pos): |
||||||
|
l=len(text) |
||||||
|
a="" |
||||||
|
print(text) |
||||||
|
while (pos<l) and (len(a)<6) and ((text[pos]>=ord("0")) and (text[pos]<=ord("9")) or (text[pos]>=ord("A")) and (text[pos]<=ord("Z"))): |
||||||
|
a+=chr(text[pos]<<1) |
||||||
|
pos+=1 |
||||||
|
while len(a)<6: a+=chr(ord(" ")<<1) #fill with spaces |
||||||
|
ssid=0 |
||||||
|
if (pos<l) and (text[pos]==ord("-")): |
||||||
|
pos+=1 |
||||||
|
if (pos<l) and (text[pos]>=ord("0")) and (text[pos]<=ord("9")): |
||||||
|
ssid+=text[pos]-ord("0") |
||||||
|
pos+=1 |
||||||
|
if (pos<l) and (text[pos]>=ord("0")) and (text[pos]<=ord("9")): |
||||||
|
ssid=ssid*10 + text[pos]-ord("0") |
||||||
|
pos+=1 |
||||||
|
if ssid>15: ssid=15 |
||||||
|
ssid=(ssid+48)<<1 |
||||||
|
if (pos<l) and (text[pos]==ord("*")): |
||||||
|
ssid|=0x80 |
||||||
|
pos+=1 |
||||||
|
a+=chr(ssid) |
||||||
|
return a, pos |
||||||
|
|
||||||
|
|
||||||
|
def udpcrc(self, frame, topos): |
||||||
|
c=0 |
||||||
|
for p in range(topos): c = (c >> 8) ^ CRCTAB[(ord(frame[p]) ^ c) & 0xff] |
||||||
|
return c |
||||||
|
def sendax(self, text, ip, values=False): |
||||||
|
a,p=self.axcall(text, 0) #src call |
||||||
|
if (p>=len(text)) or (text[p]!=ord(">")): |
||||||
|
print("fehler 1") |
||||||
|
return |
||||||
|
ax,p=self.axcall(text, p+1) #dest call |
||||||
|
ax+=a |
||||||
|
hbit=0 |
||||||
|
while True: #via calls |
||||||
|
if p>=len(text): |
||||||
|
print("found no end of address") |
||||||
|
return #found no end of address |
||||||
|
if text[p]==ord(":"): break #end of address field |
||||||
|
if text[p]!=ord(","): |
||||||
|
print("via path error") |
||||||
|
return #via path error |
||||||
|
if len(ax)>=70: |
||||||
|
print("too many via calls") |
||||||
|
return #too many via calls |
||||||
|
a,p=self.axcall(text, p+1) |
||||||
|
ax+=a |
||||||
|
hp=len(ax)-1 |
||||||
|
if (ord(ax[hp]) & 0x80)!=0: hbit=hp #store last h-bit |
||||||
|
p+=1 |
||||||
|
a="" |
||||||
|
|
||||||
|
if values: |
||||||
|
a="\x01\x30" #axudp v2 start |
||||||
|
|
||||||
|
if 'level' in values.keys(): |
||||||
|
v=values["level"] |
||||||
|
a+="V"+str(round(v))+" " #axudp v2 append level |
||||||
|
|
||||||
|
if 'quality' in values.keys(): |
||||||
|
v=values["quality"] |
||||||
|
a+="Q"+str(round(v))+" " #axudp v2 append quality |
||||||
|
|
||||||
|
if 'txdel' in values.keys(): |
||||||
|
v=values["txdel"] |
||||||
|
a+="T"+str(round(v))+" " #axudp v2 append quality |
||||||
|
|
||||||
|
if 'snr' in values.keys(): |
||||||
|
v=values["snr"] |
||||||
|
a+="S"+str(round(v))+" " #axudp v2 append snr |
||||||
|
|
||||||
|
a+="\x00" #axudp2 end |
||||||
|
|
||||||
|
i=0 |
||||||
|
for i in range(len(ax)): |
||||||
|
ch=ord(ax[i]) |
||||||
|
if (i%7==6) and (i>=20) and (i<hbit): ch|=0x80 #set h-bit on all via calls before |
||||||
|
if i+1==len(ax): ch|=1 #set ent of address bit |
||||||
|
a+=chr(ch) |
||||||
|
a+="\x03\xf0" #ui frame pid F0 |
||||||
|
i=0 |
||||||
|
while p<len(text) and i < 256: #append payload |
||||||
|
a+=chr(text[p]) |
||||||
|
p+=1 |
||||||
|
i+=1 #max 256bytes |
||||||
|
#for ch in b: print(hex(ord(ch))) |
||||||
|
sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) |
||||||
|
c=self.udpcrc(a, len(a)) |
||||||
|
a+=chr(c & 0xff) |
||||||
|
a+=chr(c>>8) |
||||||
|
sa=array("B",[0]*len(a)) |
||||||
|
for i in range(0,len(a)): sa[i]=ord(a[i]) |
||||||
|
print(sa) |
||||||
|
print(ip) |
||||||
|
res=sock.sendto(sa, ip) |
||||||
|
|
||||||
|
## RX: |
||||||
|
def callstr(self, b, p): |
||||||
|
s="" |
||||||
|
for i in range(6): |
||||||
|
ch=ord(b[p+i])>>1 |
||||||
|
if ch<32: s+="^" #show forbidden ctrl in call |
||||||
|
elif ch>32:s+=chr(ch) #call is filled with blanks |
||||||
|
ssid=(ord(b[p+6])>>1) & 0x0f |
||||||
|
if ssid: s+="-"+str(ssid) |
||||||
|
return s |
||||||
|
def axtostr(self, axbuf): |
||||||
|
b="" |
||||||
|
for x in axbuf: |
||||||
|
b+=chr(x) |
||||||
|
le=len(b) |
||||||
|
if le<2: |
||||||
|
return "" |
||||||
|
le-=2 |
||||||
|
c=self.udpcrc(b, le) |
||||||
|
if (b[le]!=chr(c & 0xff)) or (b[le+1]!=chr(c>>8)): |
||||||
|
return "" #crc error |
||||||
|
|
||||||
|
i=0 |
||||||
|
if axbuf[0]==1: #axudp v2 |
||||||
|
while (i<len(axbuf)) and (axbuf[i]!=0): i+=1 |
||||||
|
i+=1 |
||||||
|
b="" |
||||||
|
while i<len(axbuf): |
||||||
|
b+=chr(axbuf[i]) |
||||||
|
i+=1 |
||||||
|
s="" |
||||||
|
le=len(b) |
||||||
|
if le>=18: #2 calls + ctrl + pid + crc |
||||||
|
le-=2 |
||||||
|
s=self.callstr(b, 7) #src call |
||||||
|
s+=">"+self.callstr(b, 0) #destination call |
||||||
|
p=14 |
||||||
|
hbit=False |
||||||
|
while (((not (ord(b[p-1]) & 1)))) and (p+6<le): #via path |
||||||
|
if ord(b[p+6])>=128: |
||||||
|
hbit=True |
||||||
|
elif hbit: #call before had hbit |
||||||
|
s+="*" |
||||||
|
hbit=False |
||||||
|
s+=","+callstr(b, p) |
||||||
|
p+=7 |
||||||
|
if hbit: s+="*" #last call had hbit |
||||||
|
p+=2 #pid, ctrl |
||||||
|
s+=":" |
||||||
|
while p<le: #payload may contain ctrl characters |
||||||
|
s+=b[p] |
||||||
|
p+=1 |
||||||
|
return s |
||||||
|
if __name__ == '__main__': |
||||||
|
'''Test program''' |
||||||
|
import time |
||||||
|
from multiprocessing import Queue |
||||||
|
|
||||||
|
TCP_HOST = "0.0.0.0" |
||||||
|
TCP_PORT = 10001 |
||||||
|
|
||||||
|
# frames to be sent go here |
||||||
|
KissQueue = Queue() |
||||||
|
|
||||||
|
server = AXUDPServer(TCP_HOST, TCP_PORT, KissQueue) |
||||||
|
server.setDaemon(True) |
||||||
|
server.start() |
||||||
|
|
||||||
|
while True: |
||||||
|
server.send( |
||||||
|
"\xc0\x00\x82\xa0\xa4\xa6@@`\x9e\x8ar\xa8\x96\x90q\x03\xf0!4725.51N/00939.86E[322/002/A=001306 Batt=3.99V\xc0") |
||||||
|
data = KissQueue.get() |
||||||
|
print("Received KISS frame:" + repr(data)) |
@ -0,0 +1,76 @@ |
|||||||
|
# Installation and running the RPi-LoRa-Gateway |
||||||
|
|
||||||
|
## Install needed packages |
||||||
|
` |
||||||
|
sudo apt install python3 python3-rpi.gpio python3-spidev aprx screen git python3-pil python3-smbus |
||||||
|
` |
||||||
|
|
||||||
|
## Checkout the code |
||||||
|
Enter following commands:<br/> |
||||||
|
``` |
||||||
|
cd |
||||||
|
git clone https://github.com/tomelec/RPi-LoRa-KISS-TNC.git |
||||||
|
cd RPi-LoRa-KISS-TNC |
||||||
|
git clone https://github.com/mayeranalytics/pySX127x.git |
||||||
|
``` |
||||||
|
to change into homedirectory and then checkout the code and the LoRa Chip-Driver in the right directory. |
||||||
|
|
||||||
|
## Configuration |
||||||
|
Afterwards configure as following: |
||||||
|
### Edit aprx/aprx.conf.lora-aprs file |
||||||
|
Type: |
||||||
|
``` |
||||||
|
cd |
||||||
|
cd RPi-LoRa-KISS-TNC |
||||||
|
sudo cp aprx/aprx.conf.lora-aprs /etc/aprx.conf |
||||||
|
pico -w /etc/aprx.conf |
||||||
|
``` |
||||||
|
to copy and then open the config file. |
||||||
|
|
||||||
|
The most important settings are: |
||||||
|
* **mycall**<br/> |
||||||
|
Your call with an apropriate SSID suffix<br/>[Paper on SSID's from aprs.org](http://www.aprs.org/aprs11/SSIDs.txt) |
||||||
|
* **myloc**<br/> |
||||||
|
NMEA lat/lon form: |
||||||
|
``` |
||||||
|
lat ddmm.mmN lon dddmm.mmE |
||||||
|
``` |
||||||
|
Example: |
||||||
|
``` |
||||||
|
lat 4812.52N lon 01622.39E |
||||||
|
``` |
||||||
|
(simplest way to find the right coordinats for this? Go to [aprs.fi](http://www.aprs.fi) on your location right-click and choose "Add marker" then click on the marker and you should see your coordinates in the NMEA style - enter this infos without any symbols into the config file as seen in the example above) |
||||||
|
|
||||||
|
|
||||||
|
* **passcode**<br/> |
||||||
|
see [see here to generate appropiate setting](https://apps.magicbug.co.uk/passcode/) |
||||||
|
* **server**<br/> |
||||||
|
either leave the default server or if you're connected to Hamnet as well insert an APRSIS Server within the hamnet - a List of Aprs Hamnet servers can be found in the [OEVSV Wiki](http://wiki.oevsv.at/index.php/Anwendungen_am_HAMNET#APRS_Server)) |
||||||
|
|
||||||
|
|
||||||
|
to save and close the file do: |
||||||
|
`Strg + x` -> Y -> Enter |
||||||
|
|
||||||
|
### Edit driver config file |
||||||
|
Type |
||||||
|
``` |
||||||
|
pico -w pySX127x/SX127x/board_config.py |
||||||
|
``` |
||||||
|
change in line 36 |
||||||
|
from |
||||||
|
``` |
||||||
|
DIO0 = 22 # RaspPi GPIO 22 |
||||||
|
DIO0 = 5 # RaspPi GPIO 5 |
||||||
|
``` |
||||||
|
to fix the SPI connection #todo how can we config this from outside? |
||||||
|
|
||||||
|
## Start the LoRa KISS TNC and aprx server instance |
||||||
|
``` |
||||||
|
python3 Start_lora-tnc.py & |
||||||
|
sudo aprx |
||||||
|
``` |
||||||
|
|
||||||
|
## Stop the server's |
||||||
|
``` |
||||||
|
sudo killall aprx python3 |
||||||
|
``` |
@ -0,0 +1,220 @@ |
|||||||
|
# This program is free software: you can redistribute it and/or modify |
||||||
|
# it under the terms of the GNU General Public License as published by |
||||||
|
# the Free Software Foundation, either version 3 of the License, or |
||||||
|
# (at your option) any later version. |
||||||
|
# |
||||||
|
# This program is distributed in the hope that it will be useful, |
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
# GNU General Public License for more details. |
||||||
|
# |
||||||
|
# You should have received a copy of the GNU General Public License |
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||||
|
|
||||||
|
|
||||||
|
# This program provides basic KISS AX.25 APRS frame encoding and decoding. |
||||||
|
# Note that only APRS relevant structures are tested. It might not work |
||||||
|
# for generic AX.25 frames. |
||||||
|
# 11/2019 by Thomas Kottek, OE9TKH |
||||||
|
# |
||||||
|
# Inspired by: |
||||||
|
# * Python script to decode AX.25 from KISS frames over a serial TNC |
||||||
|
# https://gist.github.com/mumrah/8fe7597edde50855211e27192cce9f88 |
||||||
|
# |
||||||
|
# * Sending a raw AX.25 frame with Python |
||||||
|
# https://thomask.sdf.org/blog/2018/12/15/sending-raw-ax25-python.html |
||||||
|
# |
||||||
|
# TODO: remove escapes on decoding |
||||||
|
|
||||||
|
import struct |
||||||
|
|
||||||
|
KISS_FEND = 0xC0 # Frame start/end marker |
||||||
|
KISS_FESC = 0xDB # Escape character |
||||||
|
KISS_TFEND = 0xDC # If after an escape, means there was an 0xC0 in the source message |
||||||
|
KISS_TFESC = 0xDD # If after an escape, means there was an 0xDB in the source message |
||||||
|
|
||||||
|
|
||||||
|
# Addresses must be 6 bytes plus the SSID byte, each character shifted left by 1 |
||||||
|
# If it's the final address in the header, set the low bit to 1 |
||||||
|
# Ignoring command/response for simple example |
||||||
|
def encode_address(s, final): |
||||||
|
if b"-" not in s: |
||||||
|
s = s + b"-0" # default to SSID 0 |
||||||
|
call, ssid = s.split(b'-') |
||||||
|
if len(call) < 6: |
||||||
|
call = call + b" "*(6 - len(call)) # pad with spaces |
||||||
|
encoded_call = [x << 1 for x in call[0:6]] |
||||||
|
encoded_ssid = (int(ssid) << 1) | 0b01100000 | (0b00000001 if final else 0) |
||||||
|
return encoded_call + [encoded_ssid] |
||||||
|
|
||||||
|
|
||||||
|
def decode_address(data, cursor): |
||||||
|
(a1, a2, a3, a4, a5, a6, a7) = struct.unpack("<BBBBBBB", data[cursor:cursor + 7]) |
||||||
|
hrr = a7 >> 5 |
||||||
|
ssid = (a7 >> 1) & 0xf |
||||||
|
ext = a7 & 0x1 |
||||||
|
addr = struct.pack("<BBBBBB", a1 >> 1, a2 >> 1, a3 >> 1, a4 >> 1, a5 >> 1, a6 >> 1) |
||||||
|
if ssid != 0: |
||||||
|
call = addr.strip() + "-{}".format(ssid).encode() |
||||||
|
else: |
||||||
|
call = addr |
||||||
|
return (call, hrr, ext) |
||||||
|
|
||||||
|
|
||||||
|
def encode_kiss(frame): |
||||||
|
# Ugly frame disassembling |
||||||
|
if not b":" in frame: |
||||||
|
return None |
||||||
|
path = frame.split(b":")[0] |
||||||
|
src_addr = path.split(b">")[0] |
||||||
|
digis = path[path.find(b">") + 1:].split(b",") |
||||||
|
# destination address |
||||||
|
packet = encode_address(digis.pop(0).upper(), False) |
||||||
|
# source address |
||||||
|
packet += encode_address(path.split(b">")[0].upper(), len(digis) == 0) |
||||||
|
# digipeaters |
||||||
|
for digi in digis: |
||||||
|
final_addr = digis.index(digi) == len(digis) - 1 |
||||||
|
packet += encode_address(digi.upper(), final_addr) |
||||||
|
# control field |
||||||
|
packet += [0x03] # This is an UI frame |
||||||
|
# protocol ID |
||||||
|
packet += [0xF0] # No protocol |
||||||
|
# information field |
||||||
|
packet += frame[frame.find(b":") + 1:] |
||||||
|
|
||||||
|
# Escape the packet in case either KISS_FEND or KISS_FESC ended up in our stream |
||||||
|
packet_escaped = [] |
||||||
|
for x in packet: |
||||||
|
if x == KISS_FEND: |
||||||
|
packet_escaped += [KISS_FESC, KISS_TFEND] |
||||||
|
elif x == KISS_FESC: |
||||||
|
packet_escaped += [KISS_FESC, KISS_TFESC] |
||||||
|
else: |
||||||
|
packet_escaped += [x] |
||||||
|
|
||||||
|
# Build the frame that we will send to Dire Wolf and turn it into a string |
||||||
|
kiss_cmd = 0x00 # Two nybbles combined - TNC 0, command 0 (send data) |
||||||
|
kiss_frame = [KISS_FEND, kiss_cmd] + packet_escaped + [KISS_FEND] |
||||||
|
try: |
||||||
|
output = bytearray(kiss_frame) |
||||||
|
except ValueError: |
||||||
|
print("Invalid value in frame.") |
||||||
|
return None |
||||||
|
return output |
||||||
|
|
||||||
|
|
||||||
|
def decode_kiss(frame): |
||||||
|
result = b"" |
||||||
|
pos = 0 |
||||||
|
if frame[pos] != 0xC0 or frame[len(frame) - 1] != 0xC0: |
||||||
|
print(frame[pos], frame[len(frame) - 1]) |
||||||
|
return None |
||||||
|
pos += 1 |
||||||
|
pos += 1 |
||||||
|
|
||||||
|
# DST |
||||||
|
(dest_addr, dest_hrr, dest_ext) = decode_address(frame, pos) |
||||||
|
pos += 7 |
||||||
|
# print("DST: ", dest_addr) |
||||||
|
|
||||||
|
# SRC |
||||||
|
(src_addr, src_hrr, src_ext) = decode_address(frame, pos) |
||||||
|
pos += 7 |
||||||
|
# print("SRC: ", src_addr) |
||||||
|
|
||||||
|
result += src_addr.strip() |
||||||
|
# print(type(result), type(dest_addr.strip())) |
||||||
|
result += b">" + dest_addr.strip() |
||||||
|
|
||||||
|
# REPEATERS |
||||||
|
ext = src_ext |
||||||
|
while ext == 0: |
||||||
|
rpt_addr, rpt_hrr, ext = decode_address(frame, pos) |
||||||
|
# print("RPT: ", rpt_addr) |
||||||
|
pos += 7 |
||||||
|
result += b"," + rpt_addr.strip() |
||||||
|
|
||||||
|
result += b":" |
||||||
|
|
||||||
|
# CTRL |
||||||
|
# (ctrl,) = struct.unpack("<B", frame[pos]) |
||||||
|
ctrl = frame[pos] |
||||||
|
pos += 1 |
||||||
|
if (ctrl & 0x3) == 0x3: |
||||||
|
#(pid,) = struct.unpack("<B", frame[pos]) |
||||||
|
pid = frame[pos] |
||||||
|
# print("PID="+str(pid)) |
||||||
|
pos += 1 |
||||||
|
result += frame[pos:len(frame) - 1] |
||||||
|
elif (ctrl & 0x3) == 0x1: |
||||||
|
# decode_sframe(ctrl, frame, pos) |
||||||
|
print("SFRAME") |
||||||
|
return None |
||||||
|
elif (ctrl & 0x1) == 0x0: |
||||||
|
# decode_iframe(ctrl, frame, pos) |
||||||
|
print("IFRAME") |
||||||
|
return None |
||||||
|
|
||||||
|
return result |
||||||
|
|
||||||
|
|
||||||
|
class SerialParser(): |
||||||
|
'''Simple parser for KISS frames. It handles multiple frames in one packet |
||||||
|
and calls the callback function on each frame''' |
||||||
|
STATE_IDLE = 0 |
||||||
|
STATE_FEND = 1 |
||||||
|
STATE_DATA = 2 |
||||||
|
KISS_FEND = KISS_FEND |
||||||
|
|
||||||
|
def __init__(self, frame_cb=None): |
||||||
|
self.frame_cb = frame_cb |
||||||
|
self.reset() |
||||||
|
|
||||||
|
def reset(self): |
||||||
|
self.state = self.STATE_IDLE |
||||||
|
self.cur_frame = bytearray() |
||||||
|
|
||||||
|
def parse(self, data): |
||||||
|
'''Call parse with a string of one or more characters''' |
||||||
|
for c in data: |
||||||
|
if self.state == self.STATE_IDLE: |
||||||
|
if c == self.KISS_FEND: |
||||||
|
self.cur_frame.append(c) |
||||||
|
self.state = self.STATE_FEND |
||||||
|
elif self.state == self.STATE_FEND: |
||||||
|
if c == self.KISS_FEND: |
||||||
|
self.reset() |
||||||
|
else: |
||||||
|
self.cur_frame.append(c) |
||||||
|
self.state = self.STATE_DATA |
||||||
|
elif self.state == self.STATE_DATA: |
||||||
|
self.cur_frame.append(c) |
||||||
|
if c == self.KISS_FEND: |
||||||
|
# frame complete |
||||||
|
if self.frame_cb: |
||||||
|
self.frame_cb(self.cur_frame) |
||||||
|
self.reset() |
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__": |
||||||
|
# Playground for testing |
||||||
|
|
||||||
|
# frame = "\xc0\x00\x82\xa0\xa4\xb0dr`\x9e\x8ar\xa8\x96\x90u\x03\xf0!4725.73NR00939.61E&Experimental LoRa iGate\xc0" |
||||||
|
frame = "\xc0\x00\x82\xa0\xa4\xa6@@`\x9e\x8ar\xa8\x96\x90q\x03\xf0!4725.51N/00939.86E[322/002/A=001306 Batt=3.99V\xc0" |
||||||
|
|
||||||
|
|
||||||
|
# print(decode_kiss(frame)) |
||||||
|
# encoded = encode_kiss("OE9TKH-8>APRS,RELAY,BLA:!4725.51N/00939.86E[322/002/A=001306 Batt=3") |
||||||
|
# encoded = encode_kiss("OE9TKH-8>APRS,digi-3,digi-2:!4725.51N/00939.86E[322/002/A=001306 Batt=3") |
||||||
|
# print((decode_kiss(encoded))) |
||||||
|
|
||||||
|
# print((decode_kiss("\xc0\x00\x82\xa0\xa4\xa6@@`\x9e\x8ar\xa8\x96\x90t\xae\x92\x88\x8ab@\x03\x03\xf0}OE9GHV-10>APMI06,TCPIP,OE9TKH-10*:@110104z4726.55N/00950.63E&WX3in1 op. Holger U=14.2V,T=8.8C\xc0"))) |
||||||
|
|
||||||
|
def newframe(frame): |
||||||
|
print(repr(frame)) |
||||||
|
|
||||||
|
|
||||||
|
two_example_frames = "\xc0\x00\x82\xa0\xa4\xa6@@`\x9e\x8ar\xa8\x96\x90u\x03\xf0}SOTA>APZS16,TCPIP,OE9TKH-10*::OE9TKH-8 :<Ass/Ref> <Freq> <Mode> [call] [comment]{7ba\xc0\xc0\x00\x82\xa0\xa4\xa6@@`\x9e\x8ar\xa8\x96\x90u\x03\xf0}SOTA>APZS16,TCPIP,OE9TKH-10*::OE9TKH-8 :/mylast{7bb\xc0\xc0\x00\x82\xa0\xa4\xa6@@`\x9e\x8ar\xa8\x96\x90u\x03\xf0}SOTA>APZS16,TCPIP,OE9TKH-10*::OE9TKH-8 :/last{7bc\xc0\xc0\x00\x82\xa0\xa4\xa6@@`\x9e\x8ar\xa8\x96\x90u\x03\xf0}SOTA>APZS16,TCPIP,OE9TKH-10*::OE9TKH-8 :/time(/zone){7bd\xc0" |
||||||
|
sp = SerialParser(newframe) |
||||||
|
sp.parse(two_example_frames) |
@ -0,0 +1,140 @@ |
|||||||
|
#!/usr/bin/python |
||||||
|
# -*- coding: utf-8 -*- |
||||||
|
|
||||||
|
# This program is free software: you can redistribute it and/or modify |
||||||
|
# it under the terms of the GNU General Public License as published by |
||||||
|
# the Free Software Foundation, either version 3 of the License, or |
||||||
|
# (at your option) any later version. |
||||||
|
# |
||||||
|
# This program is distributed in the hope that it will be useful, |
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
# GNU General Public License for more details. |
||||||
|
# |
||||||
|
# You should have received a copy of the GNU General Public License |
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||||
|
|
||||||
|
import sys |
||||||
|
from asyncio import QueueEmpty |
||||||
|
import traceback |
||||||
|
sys.path.insert(0, './pySX127x/') |
||||||
|
from pySX127x.SX127x.LoRa import LoRa |
||||||
|
from pySX127x.SX127x.constants import * |
||||||
|
from pySX127x.SX127x.board_config import BOARD |
||||||
|
import time |
||||||
|
#import KissHelper |
||||||
|
|
||||||
|
|
||||||
|
class LoraAprsKissTnc(LoRa): |
||||||
|
LORA_APRS_HEADER = b"<\xff\x01" |
||||||
|
|
||||||
|
# APRS data types |
||||||
|
DATA_TYPES_POSITION = b"!'/@`" |
||||||
|
DATA_TYPE_MESSAGE = b":" |
||||||
|
DATA_TYPE_THIRD_PARTY = b"}" |
||||||
|
|
||||||
|
queue = None |
||||||
|
server = None |
||||||
|
|
||||||
|
# init has LoRa APRS default config settings - might be initialized different when creating object with parameters |
||||||
|
def __init__(self, queue, server, frequency=433.775, preamble=8, spreadingFactor=12, bandwidth=BW.BW125, |
||||||
|
codingrate=CODING_RATE.CR4_5, appendSignalReport = True, paSelect = 1, outputPower = 15, verbose=False): |
||||||
|
# Init SX127x |
||||||
|
BOARD.setup() |
||||||
|
|
||||||
|
super(LoraAprsKissTnc, self).__init__(verbose) |
||||||
|
self.queue = queue |
||||||
|
self.appendSignalReport = appendSignalReport |
||||||
|
|
||||||
|
self.set_mode(MODE.SLEEP) |
||||||
|
|
||||||
|
self.set_freq(frequency) |
||||||
|
self.set_preamble(preamble) |
||||||
|
self.set_spreading_factor(spreadingFactor) |
||||||
|
self.set_bw(bandwidth) |
||||||
|
self.set_low_data_rate_optim(True) |
||||||
|
self.set_coding_rate(codingrate) |
||||||
|
self.set_ocp_trim(100) |
||||||
|
|
||||||
|
self.set_pa_config(paSelect, outputPower) |
||||||
|
self.set_max_payload_length(255) |
||||||
|
self.set_dio_mapping([0] * 6) |
||||||
|
self.server = server |
||||||
|
|
||||||
|
self.reset_ptr_rx() |
||||||
|
self.set_mode(MODE.RXCONT) |
||||||
|
|
||||||
|
def startListening(self): |
||||||
|
try: |
||||||
|
while True: |
||||||
|
# only transmit if no signal is detected to avoid collisions |
||||||
|
if not self.get_modem_status()["signal_detected"]: |
||||||
|
# print("RSSI: %idBm" % lora.get_rssi_value()) |
||||||
|
# FIXME: Add noise floor measurement for telemetry |
||||||
|
if not self.queue.empty(): |
||||||
|
try: |
||||||
|
data = self.queue.get(block=False) |
||||||
|
if self.aprs_data_type(data) == self.DATA_TYPE_THIRD_PARTY: |
||||||
|
# remove third party thing |
||||||
|
data = data[data.find(self.DATA_TYPE_THIRD_PARTY) + 1:] |
||||||
|
data = self.LORA_APRS_HEADER + data |
||||||
|
print("LoRa TX: " + repr(data)) |
||||||
|
self.transmit(data) |
||||||
|
except QueueEmpty: |
||||||
|
pass |
||||||
|
|
||||||
|
time.sleep(0.50) |
||||||
|
except KeyboardInterrupt: |
||||||
|
BOARD.teardown() |
||||||
|
|
||||||
|
def on_rx_done(self): |
||||||
|
payload = self.read_payload(nocheck=True) |
||||||
|
if not payload: |
||||||
|
print("No Payload!") |
||||||
|
return |
||||||
|
rssi = self.get_pkt_rssi_value() |
||||||
|
snr = self.get_pkt_snr_value() |
||||||
|
data = bytes(payload) |
||||||
|
print("LoRa RX[%idBm/%idB, %ibytes]: %s" %(rssi, snr, len(data), repr(data))) |
||||||
|
|
||||||
|
flags = self.get_irq_flags() |
||||||
|
if any([flags[s] for s in ['crc_error', 'rx_timeout']]): |
||||||
|
print("Receive Error, discarding frame.") |
||||||
|
# print(self.get_irq_flags()) |
||||||
|
self.clear_irq_flags(RxDone=1, PayloadCrcError=1, RxTimeout=1) # clear rxdone IRQ flag |
||||||
|
self.reset_ptr_rx() |
||||||
|
self.set_mode(MODE.RXCONT) |
||||||
|
return |
||||||
|
|
||||||
|
if self.server: |
||||||
|
# remove LoRa-APRS header if present |
||||||
|
if data[0:len(self.LORA_APRS_HEADER)] == self.LORA_APRS_HEADER: |
||||||
|
data = data[len(self.LORA_APRS_HEADER):] |
||||||
|
if self.appendSignalReport: |
||||||
|
# Signal report only for certain frames, not messages! |
||||||
|
if self.aprs_data_type(data) in self.DATA_TYPES_POSITION: |
||||||
|
data += b" RSSI=%idBm SNR=%idB" % (rssi, snr) |
||||||
|
self.server.send(data, {"level":rssi, "snr":snr}) |
||||||
|
self.clear_irq_flags(RxDone=1) # clear rxdone IRQ flag |
||||||
|
self.reset_ptr_rx() |
||||||
|
self.set_mode(MODE.RXCONT) |
||||||
|
|
||||||
|
# self.set_mode(MODE.CAD) |
||||||
|
|
||||||
|
def on_tx_done(self): |
||||||
|
print("TX DONE") |
||||||
|
self.clear_irq_flags(TxDone=1) # clear txdone IRQ flag |
||||||
|
self.set_dio_mapping([0] * 6) |
||||||
|
self.set_mode(MODE.RXCONT) |
||||||
|
|
||||||
|
def transmit(self, data): |
||||||
|
self.write_payload([c for c in data]) |
||||||
|
self.set_dio_mapping([1, 0, 0, 0, 0, 0]) |
||||||
|
self.set_mode(MODE.TX) |
||||||
|
|
||||||
|
def aprs_data_type(self, lora_aprs_frame): |
||||||
|
delimiter_position = lora_aprs_frame.find(b":") |
||||||
|
try: |
||||||
|
return lora_aprs_frame[delimiter_position + 1] |
||||||
|
except IndexError: |
||||||
|
return "" |
@ -1,3 +1,32 @@ |
|||||||
# RPi-LoRa-KISS-TNC |
# Raspberry Pi LoRa KISS TNC |
||||||
|
|
||||||
Custom version of https://github.com/tomelec/RPi-LoRa-KISS-TNC |
This project emulates a KISS TNC and controls a hardware LoRa transceiver |
||||||
|
connected to the Raspberry´s SPI. That makes it possible to use existing |
||||||
|
software, like digipeaters, with LoRa radio communication. The KISS TNC is |
||||||
|
accessed via its TCP server. |
||||||
|
|
||||||
|
The current application is to run the KISS TNC together with the APRS digi [APRX](https://github.com/PhirePhly/aprx), which connects via TCP and provides |
||||||
|
powerful APRS digipeating and I-gate functionality for LoRa-APRS. |
||||||
|
|
||||||
|
## Hardware |
||||||
|
|
||||||
|
The LoRa KISS TNC runs on Raspberry Pi 2 or newer together with the |
||||||
|
*LoRa APRS Gateway Hat V2.0*. However, RFM98W oder RFM96W LoRa modules can |
||||||
|
be wired up to the Raspberry Pi directly aswell. See the |
||||||
|
[schematic](doc/LoRaAPRS-GW-RPI_V20_Schematic.pdf) for details. |
||||||
|
|
||||||
|
![Gateway on RPi](doc/images/LoRa-APRS_Gateway_V20.jpg) |
||||||
|
|
||||||
|
## Development |
||||||
|
|
||||||
|
This program and documentation is in a very early state and very experimental. |
||||||
|
Only the LoRa radio modem of the gateway board is supported at the moment. |
||||||
|
Display and buttons are not working. |
||||||
|
|
||||||
|
### To Do |
||||||
|
* Python 3 compatibility |
||||||
|
* Get display and buttons working |
||||||
|
* Noise floor telemetry |
||||||
|
* Installation guide and documentation |
||||||
|
* Proper configuration file |
||||||
|
* ... |
||||||
|
@ -0,0 +1,41 @@ |
|||||||
|
#!/usr/bin/python3 |
||||||
|
# -*- coding: utf-8 -*- |
||||||
|
|
||||||
|
# This program is free software: you can redistribute it and/or modify |
||||||
|
# it under the terms of the GNU General Public License as published by |
||||||
|
# the Free Software Foundation, either version 3 of the License, or |
||||||
|
# (at your option) any later version. |
||||||
|
# |
||||||
|
# This program is distributed in the hope that it will be useful, |
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
# GNU General Public License for more details. |
||||||
|
# |
||||||
|
# You should have received a copy of the GNU General Public License |
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||||
|
# |
||||||
|
# Usage: python3 Start_lora-tnc.py |
||||||
|
# |
||||||
|
from queue import Queue |
||||||
|
from TCPServer import KissServer |
||||||
|
from AXUDPServer import AXUDPServer |
||||||
|
import config |
||||||
|
from LoraAprsKissTnc import LoraAprsKissTnc |
||||||
|
|
||||||
|
# TX KISS frames go here (Digipeater -> TNC) |
||||||
|
kissQueue = Queue() |
||||||
|
|
||||||
|
# KISSTCP or AXUDP Server for the digipeater to connect |
||||||
|
if config.USE_AXUDP: |
||||||
|
server = AXUDPServer(kissQueue, config.AXUDP_LOCAL_IP, config.AXUDP_LOCAL_PORT, config.AXUDP_REMOTE_IP, config.AXUDP_REMOTE_PORT) |
||||||
|
else: |
||||||
|
server = KissServer(kissQueue, config.TCP_HOST, config.TCP_PORT) |
||||||
|
|
||||||
|
server.setDaemon(True) |
||||||
|
server.start() |
||||||
|
|
||||||
|
# LoRa transceiver instance |
||||||
|
lora = LoraAprsKissTnc(kissQueue, server, verbose=False, appendSignalReport = config.APPEND_SIGNAL_REPORT) |
||||||
|
|
||||||
|
# this call loops forever inside |
||||||
|
lora.startListening() |
@ -0,0 +1,94 @@ |
|||||||
|
#!/usr/bin/python |
||||||
|
# -*- coding: utf-8 -*- |
||||||
|
from __future__ import print_function |
||||||
|
|
||||||
|
RECV_BUFFER_LENGTH = 1024 |
||||||
|
import sys |
||||||
|
from threading import Thread |
||||||
|
import socket |
||||||
|
from KissHelper import SerialParser |
||||||
|
import KissHelper |
||||||
|
|
||||||
|
|
||||||
|
def logf(message): |
||||||
|
print(message, file=sys.stderr) |
||||||
|
|
||||||
|
|
||||||
|
class KissServer(Thread): |
||||||
|
'''TCP Server to be connected by the APRS digipeater''' |
||||||
|
|
||||||
|
txQueue = None |
||||||
|
|
||||||
|
# host and port as configured in aprx/aprx.conf.lora-aprs < interface > section |
||||||
|
def __init__(self, txQueue, host="127.0.0.1", port=10001): |
||||||
|
Thread.__init__(self) |
||||||
|
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
||||||
|
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) |
||||||
|
self.socket.bind((host, port)) |
||||||
|
self.socket.listen(1) |
||||||
|
self.data = str() |
||||||
|
self.txQueue = txQueue |
||||||
|
self.connection = None |
||||||
|
|
||||||
|
def run(self): |
||||||
|
parser = SerialParser(self.queue_frame) |
||||||
|
while True: |
||||||
|
self.connection = None |
||||||
|
self.connection, client_address = self.socket.accept() |
||||||
|
parser.reset() |
||||||
|
logf("KISS-Server: Connection from %s" % client_address[0]) |
||||||
|
while True: |
||||||
|
data = self.connection.recv(RECV_BUFFER_LENGTH) |
||||||
|
if data: |
||||||
|
parser.parse(data) |
||||||
|
else: |
||||||
|
self.connection.close() |
||||||
|
break |
||||||
|
|
||||||
|
def queue_frame(self, frame): |
||||||
|
print("KISS frame:", repr(frame)) |
||||||
|
decoded_data = KissHelper.decode_kiss(frame) |
||||||
|
print("Decoded:", decoded_data) |
||||||
|
|
||||||
|
self.txQueue.put(decoded_data, block=False) |
||||||
|
|
||||||
|
def __del__(self): |
||||||
|
self.socket.shutdown() |
||||||
|
|
||||||
|
def send(self, data, metadata): |
||||||
|
try: |
||||||
|
encoded_data = KissHelper.encode_kiss(data) |
||||||
|
except Exception as e: |
||||||
|
print("KISS encoding went wrong (exception while parsing)") |
||||||
|
traceback.print_tb(e.__traceback__) |
||||||
|
encoded_data = None |
||||||
|
|
||||||
|
if encoded_data != None: |
||||||
|
print("To Server: " + repr(encoded_data)) |
||||||
|
if self.connection: |
||||||
|
self.connection.sendall(encoded_data) |
||||||
|
else: |
||||||
|
print("KISS encoding went wrong") |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__': |
||||||
|
'''Test program''' |
||||||
|
import time |
||||||
|
from multiprocessing import Queue |
||||||
|
|
||||||
|
TCP_HOST = "0.0.0.0" |
||||||
|
TCP_PORT = 10001 |
||||||
|
|
||||||
|
# frames to be sent go here |
||||||
|
KissQueue = Queue() |
||||||
|
|
||||||
|
server = KissServer(TCP_HOST, TCP_PORT, KissQueue) |
||||||
|
server.setDaemon(True) |
||||||
|
server.start() |
||||||
|
|
||||||
|
while True: |
||||||
|
server.send( |
||||||
|
"\xc0\x00\x82\xa0\xa4\xa6@@`\x9e\x8ar\xa8\x96\x90q\x03\xf0!4725.51N/00939.86E[322/002/A=001306 Batt=3.99V\xc0") |
||||||
|
data = KissQueue.get() |
||||||
|
print("Received KISS frame:" + repr(data)) |
@ -0,0 +1,440 @@ |
|||||||
|
# |
||||||
|
# Simple sample configuration file for the APRX-2 -- an APRS iGate and Digipeater |
||||||
|
# |
||||||
|
# This configuration is structured with Apache HTTPD style tags |
||||||
|
# which then contain subsystem parameters. |
||||||
|
# |
||||||
|
|
||||||
|
# |
||||||
|
# For simple case, you need to adjust 4 things: |
||||||
|
# - Mycall parameter |
||||||
|
# - passcode parameter in APRS-IS configuration |
||||||
|
# - Select correct type of interface (ax25-device or serial-device) |
||||||
|
# - Optionally set a beacon telling where this system is |
||||||
|
# - Optionally enable digipeater with or without tx-igate |
||||||
|
# |
||||||
|
|
||||||
|
# |
||||||
|
# |
||||||
|
# Define the parameters in following order: |
||||||
|
# 1) <aprsis> ** zero or one |
||||||
|
# 2) <logging> ** zero or one |
||||||
|
# 3) <interface> ** there can be multiple! |
||||||
|
# 4) <beacon> ** zero to many |
||||||
|
# 5) <telemetry> ** zero to many |
||||||
|
# 6) <digipeater> ** zero to many (at most one for each Tx) |
||||||
|
# |
||||||
|
|
||||||
|
# |
||||||
|
# Global macro for simplified callsign definition: |
||||||
|
# Usable for 99+% of cases. |
||||||
|
# |
||||||
|
|
||||||
|
mycall N0CALL-1 |
||||||
|
|
||||||
|
# |
||||||
|
# Global macro for simplified "my location" definition in |
||||||
|
# place of explicit "lat nn lon mm" at beacons. Will also |
||||||
|
# give "my location" reference for "filter m/100". |
||||||
|
# |
||||||
|
#myloc lat ddmm.mmN lon dddmm.mmE |
||||||
|
|
||||||
|
<aprsis> |
||||||
|
# The aprsis login parameter: |
||||||
|
# Station callsignSSID used for relaying APRS frames into APRS-IS. |
||||||
|
# Use this only to define other callsign for APRS\-IS login. |
||||||
|
# |
||||||
|
#login OTHERCALL-7 # login defaults to $mycall |
||||||
|
|
||||||
|
# |
||||||
|
# Passcode for your callsign: |
||||||
|
# Unique code for your callsign to allow transmitting packets |
||||||
|
# into the APRS-IS. |
||||||
|
# |
||||||
|
passcode -1 |
||||||
|
|
||||||
|
# APRS-IS server name and optional portnumber. |
||||||
|
# |
||||||
|
# WARNING: Do not change from default port number [14580] |
||||||
|
# unless you are absolutely certain you want |
||||||
|
# something else, and you allow that something |
||||||
|
# else also affect your tx-igate behaviour! |
||||||
|
# |
||||||
|
server rotate.aprs2.net |
||||||
|
#server euro.aprs2.net |
||||||
|
#server asia.aprs2.net |
||||||
|
#server noam.aprs2.net |
||||||
|
#server soam.aprs2.net |
||||||
|
#server aunz.aprs2.net |
||||||
|
|
||||||
|
# Some APRS-IS servers tell every about 20 seconds to all contact |
||||||
|
# ports that they are there and alive. Others are just silent. |
||||||
|
# Default value is 3*"heartbeat" + some --> 120 (seconds) |
||||||
|
# |
||||||
|
#heartbeat-timeout 0 # Disabler of heartbeat timeout |
||||||
|
|
||||||
|
# APRS-IS server may support some filter commands. |
||||||
|
# See: http://www.aprs-is.net/javAPRSFilter.aspx |
||||||
|
# |
||||||
|
# You can define the filter as single long quoted string, or as |
||||||
|
# many short segments with explaining comments following them. |
||||||
|
# |
||||||
|
# Usability of these filters for a Tx-iGate is dubious, but |
||||||
|
# they exist in case you for example want to Tx-iGate packets |
||||||
|
# from some source callsigns in all cases even when they are |
||||||
|
# not in your local area. |
||||||
|
# |
||||||
|
#filter "possibly multiple filter specs in quotes" |
||||||
|
# |
||||||
|
#filter "m/100" # My-Range filter: positions within 100 km from my location |
||||||
|
#filter "f/OH2XYZ-3/50" # Friend-Range filter: 50 km of friend's last beacon position |
||||||
|
</aprsis> |
||||||
|
|
||||||
|
<logging> |
||||||
|
|
||||||
|
# pidfile is UNIX way to tell that others that this program is |
||||||
|
# running with given process-id number. This has compiled-in |
||||||
|
# default value of: pidfile /var/run/aprx.pid |
||||||
|
# |
||||||
|
pidfile /var/run/aprx.pid |
||||||
|
|
||||||
|
|
||||||
|
# rflog defines a rotatable file into which all RF-received packets |
||||||
|
# are logged. The host system can rotate it at any time without |
||||||
|
# need to signal the aprx that the file has been moved. |
||||||
|
# |
||||||
|
rflog /var/log/aprx/aprx-rf.log |
||||||
|
|
||||||
|
# aprxlog defines a rotatable file into which most important |
||||||
|
# events on APRS-IS connection are logged, namely connects and |
||||||
|
# disconnects. The host system can rotate it at any time without |
||||||
|
# need to signal the aprx that the file has been moved. |
||||||
|
# |
||||||
|
aprxlog /var/log/aprx/aprx.log |
||||||
|
|
||||||
|
# dprslog defines a rotatable file into which most important |
||||||
|
# events on DPRS receiver gateways are logged. |
||||||
|
# The host system can rotate it at any time without need to |
||||||
|
# signal the aprx that the file has been moved. |
||||||
|
# |
||||||
|
#dprslog /var/log/aprx/dprs.log |
||||||
|
|
||||||
|
# erlangfile defines a mmap():able binary file, which stores |
||||||
|
# running sums of interfaces upon which the channel erlang |
||||||
|
# estimator runs, and collects data. |
||||||
|
# Depending on the system, it may be running on a filesystem |
||||||
|
# that actually retains data over reboots, or it may not. |
||||||
|
# With this backing store, the system does not loose cumulating |
||||||
|
# erlang data over the current period, if the restart is quick, |
||||||
|
# and does not stradle any exact minute. |
||||||
|
# (Do restarts at 15 seconds over an even minute..) |
||||||
|
# This file is around 0.7 MB per each interface talking APRS. |
||||||
|
# If this file is not defined and it can not be created, |
||||||
|
# internal non-persistent in-memory storage will be used. |
||||||
|
# |
||||||
|
# Built-in default value is: /var/run/aprx.state |
||||||
|
# |
||||||
|
#erlangfile /var/run/aprx.state |
||||||
|
|
||||||
|
</logging> |
||||||
|
|
||||||
|
|
||||||
|
# *********** Multiple <interface> definitions can follow ********* |
||||||
|
|
||||||
|
# ax25-device Lists AX.25 ports by their callsigns that in Linux |
||||||
|
# systems receive APRS packets. If none are defined, |
||||||
|
# or the system is not Linux, the AX.25 network receiver |
||||||
|
# is not enabled. Used technologies need at least |
||||||
|
# Linux kernel 2.4.x |
||||||
|
# |
||||||
|
# tx-ok Boolean telling if this device is able to transmit. |
||||||
|
# |
||||||
|
|
||||||
|
#<interface> |
||||||
|
# ax25-device $mycall |
||||||
|
# #tx-ok false # transmitter enable defaults to false |
||||||
|
# #telem-to-is true # set to 'false' to disable |
||||||
|
#</interface> |
||||||
|
|
||||||
|
|
||||||
|
# |
||||||
|
# The TNC serial options. Parameters are: |
||||||
|
# - /dev/ttyUSB1 -- tty device |
||||||
|
# - 19200 -- baud rate, supported ones are: |
||||||
|
# 1200, 2400, 4800, 9600, 19200, 38400 |
||||||
|
# - 8n1 -- 8-bits, no parity, one stop-bit, |
||||||
|
# no other supported modes |
||||||
|
# - "KISS" - plain basic KISS mode |
||||||
|
# - "XORSUM" alias "BPQCRC" - KISS with BPQ "CRC" byte |
||||||
|
# - "SMACK" alias "CRC16" - KISS with real CRC |
||||||
|
# - "FLEXNET" - KISS with real CRC |
||||||
|
# - "TNC2" - TNC2 monitor format |
||||||
|
# - "DPRS" - DPRS (RX) GW |
||||||
|
# |
||||||
|
|
||||||
|
#<interface> |
||||||
|
# serial-device /dev/ttyUSB0 19200 8n1 KISS |
||||||
|
# #callsign $mycall # callsign defaults to $mycall |
||||||
|
# #tx-ok false # transmitter enable defaults to false |
||||||
|
# #telem-to-is true # set to 'false' to disable |
||||||
|
#</interface> |
||||||
|
|
||||||
|
#<interface> |
||||||
|
# serial-device /dev/ttyUSB1 19200 8n1 TNC2 |
||||||
|
# #callsign $mycall # callsign defaults to $mycall |
||||||
|
# #tx-ok false # TNC2 monitor can not have transmitter |
||||||
|
# #telem-to-is true # set to 'false' to disable |
||||||
|
#</interface> |
||||||
|
|
||||||
|
#<interface> |
||||||
|
# serial-device /dev/ttyUSB1 19200 8n1 DPRS |
||||||
|
# callsign dprsgwcallsign # must define actual callsign |
||||||
|
# #tx-ok false # DPRS monitor can not do transmit |
||||||
|
# #telem-to-is true # set to 'false' to disable |
||||||
|
#</interface> |
||||||
|
|
||||||
|
# |
||||||
|
# tcp-device behaves identically to local serial port, but allows |
||||||
|
# access to remote TCP/IP sockets. A common application is remote |
||||||
|
# KISS modems connected to Ethernet-to-serial adapters from suppliers |
||||||
|
# such as Lantronix. |
||||||
|
# It's important that this remote socket is a raw TCP socket and not |
||||||
|
# handle any byte codes as command escapes. |
||||||
|
# |
||||||
|
# tcp-device hostname portnumber mode |
||||||
|
# - hostname may be a domain name, IPv4 address, or a IPv6 address |
||||||
|
# - portnumber is any valid TCP port (1-65535) |
||||||
|
# - mode is the same as serial-device (KISS, TNC2, etc.) |
||||||
|
# |
||||||
|
|
||||||
|
#<interface> |
||||||
|
# tcp-device 192.0.2.10 10001 KISS |
||||||
|
# #callsign $mycall # callsign defaults to $mycall |
||||||
|
# #tx-ok false # transmitter enable defaults to false |
||||||
|
# #telem-to-is true # set to 'false' to disable |
||||||
|
#</interface> |
||||||
|
|
||||||
|
<interface> |
||||||
|
tcp-device 127.0.0.1 10001 KISS |
||||||
|
callsign $mycall |
||||||
|
tx-ok true |
||||||
|
telem-to-is true # set to 'false' to disable |
||||||
|
</interface> |
||||||
|
|
||||||
|
|
||||||
|
# *********** Multiple <beacon> definitions can follow ********* |
||||||
|
<beacon> |
||||||
|
# |
||||||
|
# Beacons are sent out to radio transmitters AND/OR APRSIS. |
||||||
|
# Default is "both", other modes are settable. |
||||||
|
# |
||||||
|
#beaconmode { aprsis | both | radio } |
||||||
|
# |
||||||
|
# Beacons are sent from a circullar transmission queue, total cycle time |
||||||
|
# of that queue is 20 minutes by default, and beacons are "evenly" |
||||||
|
# distributed along it. Actual intervals are randomized to be anything |
||||||
|
# in between 80% and 100% of the cycle-size / number-of-beacons. |
||||||
|
# First beacon is sent out 30 seconds after system start. |
||||||
|
# Tune the cycle-size to be suitable to your number of defined beacons. |
||||||
|
# |
||||||
|
#cycle-size 20m |
||||||
|
# |
||||||
|
# Basic beaconed thing is positional message of type "!": |
||||||
|
# |
||||||
|
#beacon symbol "R&" lat "0000.00N" lon "00000.00E" comment "Rx-only iGate" |
||||||
|
#beacon symbol "R&" $myloc comment "Rx-only iGate" |
||||||
|
# |
||||||
|
#Following are basic options: |
||||||
|
# 'symbol' no default, must be defined! |
||||||
|
# 'lat' coordinate latitude: ddmm.mmN (no default!) |
||||||
|
# 'lon' coordinate longitude: dddmm.mmE (no default!) |
||||||
|
# '$myloc' coordinate values taken from global 'myloc' entry, |
||||||
|
# and usable in place of explicit 'lat'+'lon'. |
||||||
|
# 'comment' optional tail part of the item, default is nothing |
||||||
|
# |
||||||
|
# Sample symbols: |
||||||
|
# R& is for "Rx-only iGate" |
||||||
|
# I& is for "Tx-iGate" |
||||||
|
# /# is for "Digipeater" |
||||||
|
# I# is for "Tx-iGate + Digipeater"" |
||||||
|
# |
||||||
|
#Additional options are: |
||||||
|
# 'srccall' parameter sets claimed origination address. |
||||||
|
# 'dstcall' sets destination address, default "APRXnn" |
||||||
|
# 'interface' parameter picks an interface (must be "tx-ok true" type) |
||||||
|
# 'via' sets radio distribution pattern, default: none. |
||||||
|
# 'timefix' On APRS messages with HMS timestamp (hour:min:sec), the |
||||||
|
# system fixes appropriate field with transmit time timestamp. |
||||||
|
# |
||||||
|
# Message type is by default '!', which is positional no timestamp format. |
||||||
|
# Other possible formats are definable with options: |
||||||
|
# 'type' Single character setting type: ! = / @, default: ! |
||||||
|
# 'item' Defines a name of Item (')') type beacons. |
||||||
|
# 'object' Defines a name of Object (';') type beacons. |
||||||
|
# |
||||||
|
# 'file' option tells a file at which a _raw_ APRS message content is |
||||||
|
# expected to be found as first line of text. Line ending newline |
||||||
|
# is removed, and no escapes are supported. The timefix is |
||||||
|
# available, though probably should not be used. |
||||||
|
# No \-processing is done on read text line. |
||||||
|
# |
||||||
|
# 'exec' option tells a computer program which returns to stdout _raw_ APRS |
||||||
|
# message content without newline. The timefix is |
||||||
|
# available, though probably should not be used. |
||||||
|
# No \-processing is done on read text line. |
||||||
|
# |
||||||
|
# The parameter sets can vary: |
||||||
|
# a) 'srccall nnn-n dstcall "string" symbol "R&" lat "ddmm.mmN" lon "dddmm.mmE" [comment "any text"] |
||||||
|
# b) 'srccall nnn-n dstcall "string" symbol "R&" $myloc [comment "any text"] |
||||||
|
# c) 'srccall nnn-n dstcall "string" raw "string"' |
||||||
|
# |
||||||
|
# The a) form flags on some of possible syntax errors in parameters. |
||||||
|
# It will also create only "!" type messages. The dest parameter |
||||||
|
# defaults to "APRS", but can be used to give other destinations. |
||||||
|
# The via parameter can be used to add other keywords, like "NOGATE". |
||||||
|
# |
||||||
|
# Writing correct RAW format beacon message is very hard, |
||||||
|
# which is evidenced by the frequency of bad syntax texts |
||||||
|
# people so often put there... If you can not be persuaded |
||||||
|
# not to do it, then at least VERIFY the beacon result on |
||||||
|
# web service like findu.com, or aprs.fi |
||||||
|
# |
||||||
|
# Do remember that the \ -character has special treatment in the |
||||||
|
# Aprx configuration parser. If you want a '\' on APRS content, |
||||||
|
# then you encode it on configuration file as: '\\' |
||||||
|
# |
||||||
|
# Stranger combinations with explicite "transmit this to interface X": |
||||||
|
# |
||||||
|
#beacon file /tmp/wxbeacon.txt |
||||||
|
#beacon interface N0CALL-3 srccall N0CALL-3 \ |
||||||
|
# raw "!0000.00NR00000.00E&Rx-only iGate" |
||||||
|
#beacon interface N0CALL-3 srccall N0CALL-3 \ |
||||||
|
# raw "!0000.00NI00000.00E&Tx-iGate" |
||||||
|
#beacon interface $mycall symbol "R&" $myloc \ |
||||||
|
# comment "Rx-only iGate" |
||||||
|
#beacon interface $mycall symbol "I&" $myloc \ |
||||||
|
# comment "Tx-iGate" |
||||||
|
#beacon exec /usr/bin/telemetry.pl |
||||||
|
#beacon timeout 20 exec /usr/bin/telemetry.pl |
||||||
|
#beacon interface N0CALL-3 srccall N0CALL-3 \ |
||||||
|
# timeout 20 exec /usr/bin/telemetry.pl |
||||||
|
# |
||||||
|
beaconmode aprsis |
||||||
|
cycle-size 55m |
||||||
|
beacon symbol "R&" $myloc comment "LoRa-APRS 433.775MHz/125kHz/SF12" |
||||||
|
|
||||||
|
</beacon> |
||||||
|
|
||||||
|
# *********** <telemetry> definition(s) follow ********* |
||||||
|
# |
||||||
|
# The system will always send telemetry for all of its interfaces |
||||||
|
# to APRSIS, but there is an option to define telemetry to be sent |
||||||
|
# to radio channel by using following sections for each transmitter |
||||||
|
# that is wanted to send out the telemetry. |
||||||
|
# |
||||||
|
# transmitter - callsign referring to <interface> |
||||||
|
# via - optional via-path, only 1 callsign! |
||||||
|
# source - one or more of <interface> callsigns for which |
||||||
|
# the telemetry transmission is wanted for |
||||||
|
# |
||||||
|
#<telemetry> |
||||||
|
# transmitter $mycall |
||||||
|
# via TRACE1-1 |
||||||
|
# source $mycall |
||||||
|
#</telemetry> |
||||||
|
|
||||||
|
# *********** <digipeater> definition(s) follow ********* |
||||||
|
# |
||||||
|
# The digipeater definitions tell transmitters that receive |
||||||
|
# AX.25 packets from possibly multiple sources, and then what |
||||||
|
# to do on the AX.25 headers of those messages. |
||||||
|
# |
||||||
|
# There is one transmitter per digipeater -- and inversely, there |
||||||
|
# can be at most one digipeater for each transmitter. |
||||||
|
# |
||||||
|
# In each digipeater there is at least one <source>, usually same |
||||||
|
# as the transmitter. You may use same <source> on multiple |
||||||
|
# <digipeater>s. Using multiple instances of same <source> on |
||||||
|
# a single <digipeater> does not crash the system, but it can cause |
||||||
|
# packet duplication in case of non-APRS protocols (like AX.25 CONS) |
||||||
|
# |
||||||
|
# Use only at most two levels of viscous-delay in your <digipeater>. |
||||||
|
# Immediate sending is by "0", and a delayed sending is any value |
||||||
|
# from 1 to 9. This system does not correctly support other than |
||||||
|
# immediate sending and one level of delay. |
||||||
|
# |
||||||
|
# Note: In order to igate correct when multiple receivers and |
||||||
|
# transmitters are used on single channel, the <interface> |
||||||
|
# definitions of each radio port must have associated |
||||||
|
# "igate-group N" parameter which has N of value 1 to 3. |
||||||
|
# See the aprx-manual.pdf for details. |
||||||
|
# (Default software compilation allows you to have up to |
||||||
|
# three channels of APRS operation.) |
||||||
|
# |
||||||
|
#<digipeater> |
||||||
|
# transmitter $mycall |
||||||
|
# #ratelimit 60 120 # default: average 60 packets/minute, |
||||||
|
# # # burst max 120 packets/minute |
||||||
|
# #srcratelimit 10 20 # Example: by sourcecall: |
||||||
|
# # average 10 packets/minute, |
||||||
|
# # burst max 20 packets/minute |
||||||
|
# |
||||||
|
# <source> |
||||||
|
# source $mycall |
||||||
|
# # #relay-type digipeated # default mode is "digipeated" |
||||||
|
# # viscous-delay 0 # no viscous delay for RF->RF digipeating |
||||||
|
# # ratelimit 60 120 # default: average 60 packets/minute, |
||||||
|
# # # burst max 120 packets/minute |
||||||
|
# ## filter a/la/lo/la/lo # service area filter |
||||||
|
# ## filter -b/CALL # always block these |
||||||
|
# </source> |
||||||
|
# |
||||||
|
# # Diversity receiver which combines to the primary |
||||||
|
# # Tx/Rx transmitter. There can be as many of these |
||||||
|
# # as you can connect on this machine. |
||||||
|
# #<source> |
||||||
|
# # source RXPORT-1 |
||||||
|
# # #relay-type digipeated # default mode is "digipeated" |
||||||
|
# # viscous-delay 0 # no viscous delay for RF->RF digipeating |
||||||
|
# # ratelimit 60 120 # default: average 60 packets/minute, |
||||||
|
# # # burst max 120 packets/minute |
||||||
|
# ## filter a/la/lo/la/lo # service area filter |
||||||
|
# ## filter -b/CALL # always block these |
||||||
|
# </source> |
||||||
|
# |
||||||
|
# #<source> # APRSIS source adds a TX-IGATE behaviour |
||||||
|
# # source APRSIS |
||||||
|
# # relay-type third-party # Must define this for APRSIS source! |
||||||
|
# # viscous-delay 5 # Recommendation: 5 seconds delay to give |
||||||
|
# # # RF delivery time make itself known. |
||||||
|
# # ratelimit 60 120 # default: average 60 packets/minute, |
||||||
|
# # # burst max 120 packets/minute |
||||||
|
# ## filter a/la/lo/la/lo # service area filter |
||||||
|
# ## filter -b/CALL # always block these |
||||||
|
# #</source> |
||||||
|
# |
||||||
|
# #<source> # DPRS source adds a DPRS->APRS RF gate |
||||||
|
# # interface DPRS |
||||||
|
# # ratelimit 60 120 # default: average 60 packets/minute, |
||||||
|
# # # burst max 120 packets/minute |
||||||
|
# # relay-type third-party # Must define this for DPRS source! |
||||||
|
# #</source> |
||||||
|
#</digipeater> |
||||||
|
|
||||||
|
<digipeater> |
||||||
|
transmitter $mycall |
||||||
|
<source> |
||||||
|
source $mycall |
||||||
|
relay-type digipeated # default mode is "digipeated" |
||||||
|
viscous-delay 5 # no viscous delay for RF->RF digipeating |
||||||
|
ratelimit 10 20 |
||||||
|
filter t/m |
||||||
|
</source> |
||||||
|
|
||||||
|
<source> # APRSIS source adds a TX-IGATE behaviour |
||||||
|
source APRSIS |
||||||
|
relay-type third-party # Must define this for APRSIS source! |
||||||
|
ratelimit 4 30 |
||||||
|
filter t/m |
||||||
|
</source> |
||||||
|
</digipeater> |
@ -0,0 +1,411 @@ |
|||||||
|
# |
||||||
|
# Simple sample configuration file for the APRX-2 -- an APRS iGate and Digipeater |
||||||
|
# |
||||||
|
# This configuration is structured with Apache HTTPD style tags |
||||||
|
# which then contain subsystem parameters. |
||||||
|
# |
||||||
|
|
||||||
|
# |
||||||
|
# For simple case, you need to adjust 4 things: |
||||||
|
# - Mycall parameter |
||||||
|
# - passcode parameter in APRS-IS configuration |
||||||
|
# - Select correct type of interface (ax25-device or serial-device) |
||||||
|
# - Optionally set a beacon telling where this system is |
||||||
|
# - Optionally enable digipeater with or without tx-igate |
||||||
|
# |
||||||
|
|
||||||
|
# |
||||||
|
# |
||||||
|
# Define the parameters in following order: |
||||||
|
# 1) <aprsis> ** zero or one |
||||||
|
# 2) <logging> ** zero or one |
||||||
|
# 3) <interface> ** there can be multiple! |
||||||
|
# 4) <beacon> ** zero to many |
||||||
|
# 5) <telemetry> ** zero to many |
||||||
|
# 6) <digipeater> ** zero to many (at most one for each Tx) |
||||||
|
# |
||||||
|
|
||||||
|
# |
||||||
|
# Global macro for simplified callsign definition: |
||||||
|
# Usable for 99+% of cases. |
||||||
|
# |
||||||
|
|
||||||
|
mycall N0CALL-1 |
||||||
|
|
||||||
|
# |
||||||
|
# Global macro for simplified "my location" definition in |
||||||
|
# place of explicit "lat nn lon mm" at beacons. Will also |
||||||
|
# give "my location" reference for "filter m/100". |
||||||
|
# |
||||||
|
#myloc lat ddmm.mmN lon dddmm.mmE |
||||||
|
|
||||||
|
<aprsis> |
||||||
|
# The aprsis login parameter: |
||||||
|
# Station callsignSSID used for relaying APRS frames into APRS-IS. |
||||||
|
# Use this only to define other callsign for APRS\-IS login. |
||||||
|
# |
||||||
|
#login OTHERCALL-7 # login defaults to $mycall |
||||||
|
|
||||||
|
# |
||||||
|
# Passcode for your callsign: |
||||||
|
# Unique code for your callsign to allow transmitting packets |
||||||
|
# into the APRS-IS. |
||||||
|
# |
||||||
|
passcode -1 |
||||||
|
|
||||||
|
# APRS-IS server name and optional portnumber. |
||||||
|
# |
||||||
|
# WARNING: Do not change from default port number [14580] |
||||||
|
# unless you are absolutely certain you want |
||||||
|
# something else, and you allow that something |
||||||
|
# else also affect your tx-igate behaviour! |
||||||
|
# |
||||||
|
server rotate.aprs2.net |
||||||
|
#server euro.aprs2.net |
||||||
|
#server asia.aprs2.net |
||||||
|
#server noam.aprs2.net |
||||||
|
#server soam.aprs2.net |
||||||
|
#server aunz.aprs2.net |
||||||
|
|
||||||
|
# Some APRS-IS servers tell every about 20 seconds to all contact |
||||||
|
# ports that they are there and alive. Others are just silent. |
||||||
|
# Default value is 3*"heartbeat" + some --> 120 (seconds) |
||||||
|
# |
||||||
|
#heartbeat-timeout 0 # Disabler of heartbeat timeout |
||||||
|
|
||||||
|
# APRS-IS server may support some filter commands. |
||||||
|
# See: http://www.aprs-is.net/javAPRSFilter.aspx |
||||||
|
# |
||||||
|
# You can define the filter as single long quoted string, or as |
||||||
|
# many short segments with explaining comments following them. |
||||||
|
# |
||||||
|
# Usability of these filters for a Tx-iGate is dubious, but |
||||||
|
# they exist in case you for example want to Tx-iGate packets |
||||||
|
# from some source callsigns in all cases even when they are |
||||||
|
# not in your local area. |
||||||
|
# |
||||||
|
#filter "possibly multiple filter specs in quotes" |
||||||
|
# |
||||||
|
#filter "m/100" # My-Range filter: positions within 100 km from my location |
||||||
|
#filter "f/OH2XYZ-3/50" # Friend-Range filter: 50 km of friend's last beacon position |
||||||
|
</aprsis> |
||||||
|
|
||||||
|
<logging> |
||||||
|
|
||||||
|
# pidfile is UNIX way to tell that others that this program is |
||||||
|
# running with given process-id number. This has compiled-in |
||||||
|
# default value of: pidfile /var/run/aprx.pid |
||||||
|
# |
||||||
|
pidfile /var/run/aprx.pid |
||||||
|
|
||||||
|
|
||||||
|
# rflog defines a rotatable file into which all RF-received packets |
||||||
|
# are logged. The host system can rotate it at any time without |
||||||
|
# need to signal the aprx that the file has been moved. |
||||||
|
# |
||||||
|
rflog /var/log/aprx/aprx-rf.log |
||||||
|
|
||||||
|
# aprxlog defines a rotatable file into which most important |
||||||
|
# events on APRS-IS connection are logged, namely connects and |
||||||
|
# disconnects. The host system can rotate it at any time without |
||||||
|
# need to signal the aprx that the file has been moved. |
||||||
|
# |
||||||
|
aprxlog /var/log/aprx/aprx.log |
||||||
|
|
||||||
|
# dprslog defines a rotatable file into which most important |
||||||
|
# events on DPRS receiver gateways are logged. |
||||||
|
# The host system can rotate it at any time without need to |
||||||
|
# signal the aprx that the file has been moved. |
||||||
|
# |
||||||
|
#dprslog /var/log/aprx/dprs.log |
||||||
|
|
||||||
|
# erlangfile defines a mmap():able binary file, which stores |
||||||
|
# running sums of interfaces upon which the channel erlang |
||||||
|
# estimator runs, and collects data. |
||||||
|
# Depending on the system, it may be running on a filesystem |
||||||
|
# that actually retains data over reboots, or it may not. |
||||||
|
# With this backing store, the system does not loose cumulating |
||||||
|
# erlang data over the current period, if the restart is quick, |
||||||
|
# and does not stradle any exact minute. |
||||||
|
# (Do restarts at 15 seconds over an even minute..) |
||||||
|
# This file is around 0.7 MB per each interface talking APRS. |
||||||
|
# If this file is not defined and it can not be created, |
||||||
|
# internal non-persistent in-memory storage will be used. |
||||||
|
# |
||||||
|
# Built-in default value is: /var/run/aprx.state |
||||||
|
# |
||||||
|
#erlangfile /var/run/aprx.state |
||||||
|
|
||||||
|
</logging> |
||||||
|
|
||||||
|
|
||||||
|
# *********** Multiple <interface> definitions can follow ********* |
||||||
|
|
||||||
|
# ax25-device Lists AX.25 ports by their callsigns that in Linux |
||||||
|
# systems receive APRS packets. If none are defined, |
||||||
|
# or the system is not Linux, the AX.25 network receiver |
||||||
|
# is not enabled. Used technologies need at least |
||||||
|
# Linux kernel 2.4.x |
||||||
|
# |
||||||
|
# tx-ok Boolean telling if this device is able to transmit. |
||||||
|
# |
||||||
|
|
||||||
|
#<interface> |
||||||
|
# ax25-device $mycall |
||||||
|
# #tx-ok false # transmitter enable defaults to false |
||||||
|
# #telem-to-is true # set to 'false' to disable |
||||||
|
#</interface> |
||||||
|
|
||||||
|
|
||||||
|
# |
||||||
|
# The TNC serial options. Parameters are: |
||||||
|
# - /dev/ttyUSB1 -- tty device |
||||||
|
# - 19200 -- baud rate, supported ones are: |
||||||
|
# 1200, 2400, 4800, 9600, 19200, 38400 |
||||||
|
# - 8n1 -- 8-bits, no parity, one stop-bit, |
||||||
|
# no other supported modes |
||||||
|
# - "KISS" - plain basic KISS mode |
||||||
|
# - "XORSUM" alias "BPQCRC" - KISS with BPQ "CRC" byte |
||||||
|
# - "SMACK" alias "CRC16" - KISS with real CRC |
||||||
|
# - "FLEXNET" - KISS with real CRC |
||||||
|
# - "TNC2" - TNC2 monitor format |
||||||
|
# - "DPRS" - DPRS (RX) GW |
||||||
|
# |
||||||
|
|
||||||
|
#<interface> |
||||||
|
# serial-device /dev/ttyUSB0 19200 8n1 KISS |
||||||
|
# #callsign $mycall # callsign defaults to $mycall |
||||||
|
# #tx-ok false # transmitter enable defaults to false |
||||||
|
# #telem-to-is true # set to 'false' to disable |
||||||
|
#</interface> |
||||||
|
|
||||||
|
#<interface> |
||||||
|
# serial-device /dev/ttyUSB1 19200 8n1 TNC2 |
||||||
|
# #callsign $mycall # callsign defaults to $mycall |
||||||
|
# #tx-ok false # TNC2 monitor can not have transmitter |
||||||
|
# #telem-to-is true # set to 'false' to disable |
||||||
|
#</interface> |
||||||
|
|
||||||
|
#<interface> |
||||||
|
# serial-device /dev/ttyUSB1 19200 8n1 DPRS |
||||||
|
# callsign dprsgwcallsign # must define actual callsign |
||||||
|
# #tx-ok false # DPRS monitor can not do transmit |
||||||
|
# #telem-to-is true # set to 'false' to disable |
||||||
|
#</interface> |
||||||
|
|
||||||
|
# |
||||||
|
# tcp-device behaves identically to local serial port, but allows |
||||||
|
# access to remote TCP/IP sockets. A common application is remote |
||||||
|
# KISS modems connected to Ethernet-to-serial adapters from suppliers |
||||||
|
# such as Lantronix. |
||||||
|
# It's important that this remote socket is a raw TCP socket and not |
||||||
|
# handle any byte codes as command escapes. |
||||||
|
# |
||||||
|
# tcp-device hostname portnumber mode |
||||||
|
# - hostname may be a domain name, IPv4 address, or a IPv6 address |
||||||
|
# - portnumber is any valid TCP port (1-65535) |
||||||
|
# - mode is the same as serial-device (KISS, TNC2, etc.) |
||||||
|
# |
||||||
|
|
||||||
|
#<interface> |
||||||
|
# tcp-device 192.0.2.10 10001 KISS |
||||||
|
# #callsign $mycall # callsign defaults to $mycall |
||||||
|
# #tx-ok false # transmitter enable defaults to false |
||||||
|
# #telem-to-is true # set to 'false' to disable |
||||||
|
#</interface> |
||||||
|
|
||||||
|
|
||||||
|
# *********** Multiple <beacon> definitions can follow ********* |
||||||
|
<beacon> |
||||||
|
# |
||||||
|
# Beacons are sent out to radio transmitters AND/OR APRSIS. |
||||||
|
# Default is "both", other modes are settable. |
||||||
|
# |
||||||
|
#beaconmode { aprsis | both | radio } |
||||||
|
# |
||||||
|
# Beacons are sent from a circullar transmission queue, total cycle time |
||||||
|
# of that queue is 20 minutes by default, and beacons are "evenly" |
||||||
|
# distributed along it. Actual intervals are randomized to be anything |
||||||
|
# in between 80% and 100% of the cycle-size / number-of-beacons. |
||||||
|
# First beacon is sent out 30 seconds after system start. |
||||||
|
# Tune the cycle-size to be suitable to your number of defined beacons. |
||||||
|
# |
||||||
|
#cycle-size 20m |
||||||
|
# |
||||||
|
# Basic beaconed thing is positional message of type "!": |
||||||
|
# |
||||||
|
#beacon symbol "R&" lat "0000.00N" lon "00000.00E" comment "Rx-only iGate" |
||||||
|
#beacon symbol "R&" $myloc comment "Rx-only iGate" |
||||||
|
# |
||||||
|
#Following are basic options: |
||||||
|
# 'symbol' no default, must be defined! |
||||||
|
# 'lat' coordinate latitude: ddmm.mmN (no default!) |
||||||
|
# 'lon' coordinate longitude: dddmm.mmE (no default!) |
||||||
|
# '$myloc' coordinate values taken from global 'myloc' entry, |
||||||
|
# and usable in place of explicit 'lat'+'lon'. |
||||||
|
# 'comment' optional tail part of the item, default is nothing |
||||||
|
# |
||||||
|
# Sample symbols: |
||||||
|
# R& is for "Rx-only iGate" |
||||||
|
# I& is for "Tx-iGate" |
||||||
|
# /# is for "Digipeater" |
||||||
|
# I# is for "Tx-iGate + Digipeater"" |
||||||
|
# |
||||||
|
#Additional options are: |
||||||
|
# 'srccall' parameter sets claimed origination address. |
||||||
|
# 'dstcall' sets destination address, default "APRXnn" |
||||||
|
# 'interface' parameter picks an interface (must be "tx-ok true" type) |
||||||
|
# 'via' sets radio distribution pattern, default: none. |
||||||
|
# 'timefix' On APRS messages with HMS timestamp (hour:min:sec), the |
||||||
|
# system fixes appropriate field with transmit time timestamp. |
||||||
|
# |
||||||
|
# Message type is by default '!', which is positional no timestamp format. |
||||||
|
# Other possible formats are definable with options: |
||||||
|
# 'type' Single character setting type: ! = / @, default: ! |
||||||
|
# 'item' Defines a name of Item (')') type beacons. |
||||||
|
# 'object' Defines a name of Object (';') type beacons. |
||||||
|
# |
||||||
|
# 'file' option tells a file at which a _raw_ APRS message content is |
||||||
|
# expected to be found as first line of text. Line ending newline |
||||||
|
# is removed, and no escapes are supported. The timefix is |
||||||
|
# available, though probably should not be used. |
||||||
|
# No \-processing is done on read text line. |
||||||
|
# |
||||||
|
# 'exec' option tells a computer program which returns to stdout _raw_ APRS |
||||||
|
# message content without newline. The timefix is |
||||||
|
# available, though probably should not be used. |
||||||
|
# No \-processing is done on read text line. |
||||||
|
# |
||||||
|
# The parameter sets can vary: |
||||||
|
# a) 'srccall nnn-n dstcall "string" symbol "R&" lat "ddmm.mmN" lon "dddmm.mmE" [comment "any text"] |
||||||
|
# b) 'srccall nnn-n dstcall "string" symbol "R&" $myloc [comment "any text"] |
||||||
|
# c) 'srccall nnn-n dstcall "string" raw "string"' |
||||||
|
# |
||||||
|
# The a) form flags on some of possible syntax errors in parameters. |
||||||
|
# It will also create only "!" type messages. The dest parameter |
||||||
|
# defaults to "APRS", but can be used to give other destinations. |
||||||
|
# The via parameter can be used to add other keywords, like "NOGATE". |
||||||
|
# |
||||||
|
# Writing correct RAW format beacon message is very hard, |
||||||
|
# which is evidenced by the frequency of bad syntax texts |
||||||
|
# people so often put there... If you can not be persuaded |
||||||
|
# not to do it, then at least VERIFY the beacon result on |
||||||
|
# web service like findu.com, or aprs.fi |
||||||
|
# |
||||||
|
# Do remember that the \ -character has special treatment in the |
||||||
|
# Aprx configuration parser. If you want a '\' on APRS content, |
||||||
|
# then you encode it on configuration file as: '\\' |
||||||
|
# |
||||||
|
# Stranger combinations with explicite "transmit this to interface X": |
||||||
|
# |
||||||
|
#beacon file /tmp/wxbeacon.txt |
||||||
|
#beacon interface N0CALL-3 srccall N0CALL-3 \ |
||||||
|
# raw "!0000.00NR00000.00E&Rx-only iGate" |
||||||
|
#beacon interface N0CALL-3 srccall N0CALL-3 \ |
||||||
|
# raw "!0000.00NI00000.00E&Tx-iGate" |
||||||
|
#beacon interface $mycall symbol "R&" $myloc \ |
||||||
|
# comment "Rx-only iGate" |
||||||
|
#beacon interface $mycall symbol "I&" $myloc \ |
||||||
|
# comment "Tx-iGate" |
||||||
|
#beacon exec /usr/bin/telemetry.pl |
||||||
|
#beacon timeout 20 exec /usr/bin/telemetry.pl |
||||||
|
#beacon interface N0CALL-3 srccall N0CALL-3 \ |
||||||
|
# timeout 20 exec /usr/bin/telemetry.pl |
||||||
|
# |
||||||
|
</beacon> |
||||||
|
|
||||||
|
# *********** <telemetry> definition(s) follow ********* |
||||||
|
# |
||||||
|
# The system will always send telemetry for all of its interfaces |
||||||
|
# to APRSIS, but there is an option to define telemetry to be sent |
||||||
|
# to radio channel by using following sections for each transmitter |
||||||
|
# that is wanted to send out the telemetry. |
||||||
|
# |
||||||
|
# transmitter - callsign referring to <interface> |
||||||
|
# via - optional via-path, only 1 callsign! |
||||||
|
# source - one or more of <interface> callsigns for which |
||||||
|
# the telemetry transmission is wanted for |
||||||
|
# |
||||||
|
#<telemetry> |
||||||
|
# transmitter $mycall |
||||||
|
# via TRACE1-1 |
||||||
|
# source $mycall |
||||||
|
#</telemetry> |
||||||
|
|
||||||
|
# *********** <digipeater> definition(s) follow ********* |
||||||
|
# |
||||||
|
# The digipeater definitions tell transmitters that receive |
||||||
|
# AX.25 packets from possibly multiple sources, and then what |
||||||
|
# to do on the AX.25 headers of those messages. |
||||||
|
# |
||||||
|
# There is one transmitter per digipeater -- and inversely, there |
||||||
|
# can be at most one digipeater for each transmitter. |
||||||
|
# |
||||||
|
# In each digipeater there is at least one <source>, usually same |
||||||
|
# as the transmitter. You may use same <source> on multiple |
||||||
|
# <digipeater>s. Using multiple instances of same <source> on |
||||||
|
# a single <digipeater> does not crash the system, but it can cause |
||||||
|
# packet duplication in case of non-APRS protocols (like AX.25 CONS) |
||||||
|
# |
||||||
|
# Use only at most two levels of viscous-delay in your <digipeater>. |
||||||
|
# Immediate sending is by "0", and a delayed sending is any value |
||||||
|
# from 1 to 9. This system does not correctly support other than |
||||||
|
# immediate sending and one level of delay. |
||||||
|
# |
||||||
|
# Note: In order to igate correct when multiple receivers and |
||||||
|
# transmitters are used on single channel, the <interface> |
||||||
|
# definitions of each radio port must have associated |
||||||
|
# "igate-group N" parameter which has N of value 1 to 3. |
||||||
|
# See the aprx-manual.pdf for details. |
||||||
|
# (Default software compilation allows you to have up to |
||||||
|
# three channels of APRS operation.) |
||||||
|
# |
||||||
|
#<digipeater> |
||||||
|
# transmitter $mycall |
||||||
|
# #ratelimit 60 120 # default: average 60 packets/minute, |
||||||
|
# # # burst max 120 packets/minute |
||||||
|
# #srcratelimit 10 20 # Example: by sourcecall: |
||||||
|
# # average 10 packets/minute, |
||||||
|
# # burst max 20 packets/minute |
||||||
|
# |
||||||
|
# <source> |
||||||
|
# source $mycall |
||||||
|
# # #relay-type digipeated # default mode is "digipeated" |
||||||
|
# # viscous-delay 0 # no viscous delay for RF->RF digipeating |
||||||
|
# # ratelimit 60 120 # default: average 60 packets/minute, |
||||||
|
# # # burst max 120 packets/minute |
||||||
|
# ## filter a/la/lo/la/lo # service area filter |
||||||
|
# ## filter -b/CALL # always block these |
||||||
|
# </source> |
||||||
|
# |
||||||
|
# # Diversity receiver which combines to the primary |
||||||
|
# # Tx/Rx transmitter. There can be as many of these |
||||||
|
# # as you can connect on this machine. |
||||||
|
# #<source> |
||||||
|
# # source RXPORT-1 |
||||||
|
# # #relay-type digipeated # default mode is "digipeated" |
||||||
|
# # viscous-delay 0 # no viscous delay for RF->RF digipeating |
||||||
|
# # ratelimit 60 120 # default: average 60 packets/minute, |
||||||
|
# # # burst max 120 packets/minute |
||||||
|
# ## filter a/la/lo/la/lo # service area filter |
||||||
|
# ## filter -b/CALL # always block these |
||||||
|
# </source> |
||||||
|
# |
||||||
|
# #<source> # APRSIS source adds a TX-IGATE behaviour |
||||||
|
# # source APRSIS |
||||||
|
# # relay-type third-party # Must define this for APRSIS source! |
||||||
|
# # viscous-delay 5 # Recommendation: 5 seconds delay to give |
||||||
|
# # # RF delivery time make itself known. |
||||||
|
# # ratelimit 60 120 # default: average 60 packets/minute, |
||||||
|
# # # burst max 120 packets/minute |
||||||
|
# ## filter a/la/lo/la/lo # service area filter |
||||||
|
# ## filter -b/CALL # always block these |
||||||
|
# #</source> |
||||||
|
# |
||||||
|
# #<source> # DPRS source adds a DPRS->APRS RF gate |
||||||
|
# # interface DPRS |
||||||
|
# # ratelimit 60 120 # default: average 60 packets/minute, |
||||||
|
# # # burst max 120 packets/minute |
||||||
|
# # relay-type third-party # Must define this for DPRS source! |
||||||
|
# #</source> |
||||||
|
#</digipeater> |
@ -0,0 +1,24 @@ |
|||||||
|
## KISS Settings |
||||||
|
# Where to listen? |
||||||
|
# TCP_HOST can be "localhost", "0.0.0.0" or a specific interface address |
||||||
|
# TCP_PORT as configured in aprx.conf <interface> section |
||||||
|
TCP_HOST = "0.0.0.0" |
||||||
|
TCP_PORT = 10001 |
||||||
|
|
||||||
|
## AXUDP Settings |
||||||
|
# AXUDP_REMOTE_IP IP to wich udp packets are sent |
||||||
|
# AXUDP_REMOTE_PORT UDP Port to wich udp packets are sent |
||||||
|
# AXUDP_LOCAL_IP IP of Interface to listen on, 0.0.0.0 for all interfaces |
||||||
|
# AXUDP_LOCAL_PORT Port to listen for incoming AXUDP packets |
||||||
|
|
||||||
|
AXUDP_REMOTE_IP = "192.168.0.185" |
||||||
|
AXUDP_REMOTE_PORT = 20000 |
||||||
|
AXUDP_LOCAL_IP = "0.0.0.0" |
||||||
|
AXUDP_LOCAL_PORT = 20000 |
||||||
|
|
||||||
|
## Genral Settings |
||||||
|
# USE_AXUDP Switch from KISS to AXUDP if True |
||||||
|
# APPEND_SIGNAL_REPORT adds signal report to text of APRS-Message for debug purpose |
||||||
|
# this will change the original message and could cause loops |
||||||
|
USE_AXUDP = True |
||||||
|
APPEND_SIGNAL_REPORT = True |
Binary file not shown.
After Width: | Height: | Size: 161 KiB |
@ -0,0 +1,54 @@ |
|||||||
|
|
||||||
|
# Experimental multi-SF reception. Does not yet work. |
||||||
|
# lora.set_mode(MODE.STDBY) |
||||||
|
# lora.set_mode(MODE.CAD) |
||||||
|
# f = lora.get_irq_flags() |
||||||
|
# while True: |
||||||
|
# |
||||||
|
# for sf in [10, 11, 12]: |
||||||
|
# #m = lora.get_mode() # important to keep pySX127x updated |
||||||
|
# lora.set_mode(MODE.STDBY) |
||||||
|
# lora.set_spreading_factor(sf) |
||||||
|
# lora.set_mode(MODE.CAD) |
||||||
|
# #time.sleep(0.001) |
||||||
|
# while f["cad_done"] == 0: |
||||||
|
# time.sleep(0.05) |
||||||
|
# f = lora.get_irq_flags() |
||||||
|
# #print(sf, MODE.lookup[lora.get_mode()], f) |
||||||
|
# if f["cad_detected"]: |
||||||
|
# print("DET SF %i" % sf) |
||||||
|
# lora.set_mode(MODE.RXSINGLE) |
||||||
|
# time.sleep(3) |
||||||
|
# lora.clear_irq_flags(CadDone=1, CadDetected=1) |
||||||
|
# f = lora.get_irq_flags() |
||||||
|
# #print((lora.get_irq_flags())) |
||||||
|
# |
||||||
|
# import sys |
||||||
|
# sys.exit(0) |
||||||
|
|
||||||
|
# if f["cad_done"]: |
||||||
|
# lora.clear_irq_flags(CadDone=1) |
||||||
|
# if f["cad_detected"]: |
||||||
|
# lora.clear_irq_flags(CadDetected=1) |
||||||
|
# lora.set_mode(MODE.RXSINGLE) |
||||||
|
# else: |
||||||
|
# if mode == 1: |
||||||
|
# lora.set_spreading_factor(SPREADINGFACTOR) |
||||||
|
# lora.set_low_data_rate_optim(True) |
||||||
|
# mode = 2 |
||||||
|
# else: |
||||||
|
# lora.set_spreading_factor(10) |
||||||
|
# lora.set_low_data_rate_optim(False) |
||||||
|
# mode = 1 |
||||||
|
# lora.set_mode(MODE.CAD) |
||||||
|
# elif f["rx_timeout"]: |
||||||
|
# lora.clear_irq_flags(RxTimeout=1) |
||||||
|
# lora.set_mode(MODE.CAD) |
||||||
|
# |
||||||
|
# if f["valid_header"]: |
||||||
|
# lora.clear_irq_flags(ValidHeader=1) |
||||||
|
|
||||||
|
|
||||||
|
# if lora.mode == MODE.RXCONT: |
||||||
|
# lora.set_mode(MODE.CAD) |
||||||
|
# time.sleep(0.001) |
Loading…
Reference in new issue