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