kopia lustrzana https://github.com/projecthorus/wenet
Much faster LDPC encoder.
rodzic
72a1e00678
commit
2afb7bdcdd
File diff suppressed because one or more lines are too long
33
PacketTX.py
33
PacketTX.py
|
@ -5,6 +5,13 @@
|
||||||
# Frames packets (preamble, unique word, checksum)
|
# Frames packets (preamble, unique word, checksum)
|
||||||
# and transmits them out of a serial port.
|
# and transmits them out of a serial port.
|
||||||
#
|
#
|
||||||
|
# RPI UART Calibration
|
||||||
|
# 9600 -> 9600.1536
|
||||||
|
# 19200 -> 19200.307
|
||||||
|
# 38400 -> 38339.148
|
||||||
|
# 57600 -> 57693.417
|
||||||
|
# 115200 -> 115386.834
|
||||||
|
#
|
||||||
# Mark Jessop <vk5qi@rfhead.net>
|
# Mark Jessop <vk5qi@rfhead.net>
|
||||||
#
|
#
|
||||||
|
|
||||||
|
@ -34,9 +41,9 @@ class BinaryDebug(object):
|
||||||
self.f.close()
|
self.f.close()
|
||||||
|
|
||||||
def write_debug_message(message, debug_file = "tx_idle_message.txt"):
|
def write_debug_message(message, debug_file = "tx_idle_message.txt"):
|
||||||
f = open(debug_file,'w')
|
#f = open(debug_file,'w')
|
||||||
f.write(message)
|
#f.write(message)
|
||||||
f.close()
|
#f.close()
|
||||||
print("DEBUG MSG: %s" % message)
|
print("DEBUG MSG: %s" % message)
|
||||||
|
|
||||||
class PacketTX(object):
|
class PacketTX(object):
|
||||||
|
@ -59,8 +66,9 @@ class PacketTX(object):
|
||||||
self.payload_length = payload_length
|
self.payload_length = payload_length
|
||||||
|
|
||||||
self.crc16 = crcmod.predefined.mkCrcFun('crc-ccitt-false')
|
self.crc16 = crcmod.predefined.mkCrcFun('crc-ccitt-false')
|
||||||
self.fec = fec
|
|
||||||
self.callsign = callsign
|
self.callsign = callsign
|
||||||
|
self.idle_message = "DE %s" % callsign
|
||||||
|
self.fec = fec
|
||||||
|
|
||||||
def start_tx(self):
|
def start_tx(self):
|
||||||
self.transmit_active = True
|
self.transmit_active = True
|
||||||
|
@ -82,18 +90,17 @@ class PacketTX(object):
|
||||||
else:
|
else:
|
||||||
return self.preamble + self.unique_word + packet + crc
|
return self.preamble + self.unique_word + packet + crc
|
||||||
|
|
||||||
|
|
||||||
|
def set_idle_message(self, message):
|
||||||
|
temp_msg = "\x00" + "DE %s: \t%s" % (self.callsign, message)
|
||||||
|
self.idle_message = self.frame_packet(temp_msg)
|
||||||
|
|
||||||
|
|
||||||
# Either generate an idle message, or read one in from a file (tx_idle_message.txt) if it exists.
|
# Either generate an idle message, or read one in from a file (tx_idle_message.txt) if it exists.
|
||||||
# This might be a useful way of getting error messages down from the payload.
|
# This might be a useful way of getting error messages down from the payload.
|
||||||
def generate_idle_message(self):
|
def generate_idle_message(self):
|
||||||
# Try and read in a message from a file.
|
|
||||||
try:
|
|
||||||
f = open("tx_idle_message.txt")
|
|
||||||
idle_data = "DE %s: \t%s" % (self.callsign,f.read())
|
|
||||||
f.close()
|
|
||||||
except:
|
|
||||||
idle_data = "DE %s Wenet High-Speed FSK Transmitter" % self.callsign
|
|
||||||
# Append a \x00 control code before the data
|
# Append a \x00 control code before the data
|
||||||
return "\x00" + idle_data
|
return "\x00" + "DE %s: \t%s" % (self.callsign,self.idle_message)
|
||||||
|
|
||||||
|
|
||||||
def tx_thread(self):
|
def tx_thread(self):
|
||||||
|
@ -104,7 +111,7 @@ class PacketTX(object):
|
||||||
else:
|
else:
|
||||||
if not self.debug:
|
if not self.debug:
|
||||||
#self.s.write(self.idle_sequence)
|
#self.s.write(self.idle_sequence)
|
||||||
self.s.write(self.frame_packet(self.generate_idle_message()))
|
self.s.write(self.idle_message)
|
||||||
else:
|
else:
|
||||||
sleep(0.05)
|
sleep(0.05)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
|
||||||
|
LDPC Encoder, using a 'RA' encoder written by Bill Cowley VK5DSP in March 2016.
|
||||||
|
|
||||||
|
Compile with:
|
||||||
|
gcc -fPIC -shared -o ldpc_enc.so ldpc_enc.c
|
||||||
|
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include<stdio.h>
|
||||||
|
#include<stdlib.h>
|
||||||
|
#define Nibits 2064
|
||||||
|
#define Npbits 516
|
||||||
|
#define Nwt 12
|
||||||
|
|
||||||
|
|
||||||
|
unsigned short hrows[] = {
|
||||||
|
// read from file created via make_Hrows_txt.m
|
||||||
|
// use the new code of March 2016
|
||||||
|
#include "Hrow2064.txt"
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
void encode(unsigned char *ibits, unsigned char *pbits) {
|
||||||
|
unsigned int p, i, tmp, par, prev=0;
|
||||||
|
char c;
|
||||||
|
for (p=0; p<Npbits; p++) {
|
||||||
|
par =0;
|
||||||
|
for (i=0; i<Nwt; i++)
|
||||||
|
par = par + ibits[hrows[p*Nwt+i]-1];
|
||||||
|
// -1 as matlab arrays start from 1, C from 0
|
||||||
|
tmp = par + prev;
|
||||||
|
//printf(" p ind %d, parity %d \n", p, tmp);
|
||||||
|
//c = getchar();
|
||||||
|
tmp &= 1; // only retain the lsb
|
||||||
|
prev = tmp;
|
||||||
|
pbits[p] =tmp;
|
||||||
|
}
|
||||||
|
}
|
112
ldpc_encoder.py
112
ldpc_encoder.py
|
@ -1,94 +1,53 @@
|
||||||
#!/usr/bin/env python2.7
|
#!/usr/bin/env python
|
||||||
#
|
#
|
||||||
# LDPC Encoder based on C code from Bill Cowley in March 2016
|
# LDPC Encoder Functions.
|
||||||
# Author: Mark Jessop <vk5qi@rfhead.net>
|
# Uses ctypes to call the encode function from ldpc_enc.c
|
||||||
#
|
#
|
||||||
|
# ldpc_enc.c needs to be compiled to a .so before this will work, with:
|
||||||
|
# gcc -fPIC -shared -o ldpc_enc.so ldpc_enc.c
|
||||||
|
#
|
||||||
|
# Mark Jessop <vk5qi@rfhead.net>
|
||||||
|
#
|
||||||
|
|
||||||
|
import ctypes
|
||||||
|
from numpy.ctypeslib import ndpointer
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
import time
|
||||||
|
|
||||||
|
|
||||||
|
# Attempt to load in ldpc_enc.so on startup.
|
||||||
|
try:
|
||||||
|
_ldpc_enc = ctypes.CDLL("./ldpc_enc.so")
|
||||||
|
_ldpc_enc.encode.restype = None
|
||||||
|
_ldpc_enc.encode.argtypes = (ndpointer(ctypes.c_ubyte, flags="C_CONTIGUOUS"), ndpointer(ctypes.c_ubyte, flags="C_CONTIGUOUS"))
|
||||||
|
except OSError as e:
|
||||||
|
raise OSError("Could not find ldpc_enc.so! Have you compiled ldpc_enc.c?")
|
||||||
|
|
||||||
#
|
#
|
||||||
# ORIGINAL C CODE FOLLOWS
|
# LDPC Encoder.
|
||||||
|
# Accepts a 258 byte string as input, returns the LDPC parity bits.
|
||||||
#
|
#
|
||||||
|
|
||||||
# #define Nibits 2064
|
def ldpc_encode_string(payload, Nibits = 2064, Npbits = 516):
|
||||||
# #define Npbits 516
|
|
||||||
# #define Nwt 12
|
|
||||||
|
|
||||||
|
|
||||||
# unsigned short hrows[] = {
|
|
||||||
# // read from file created via make_Hrows_txt.m
|
|
||||||
# // use the new code of March 2016
|
|
||||||
# #include "Hrow2064.txt"
|
|
||||||
# };
|
|
||||||
|
|
||||||
# unsigned char ibits[Nibits]; // info array
|
|
||||||
# unsigned char pbits[Npbits]; // parity array
|
|
||||||
|
|
||||||
# void encode() {
|
|
||||||
# unsigned int p, i, tmp, par, prev=0;
|
|
||||||
# char c;
|
|
||||||
# for (p=0; p<Npbits; p++) {
|
|
||||||
# par =0;
|
|
||||||
# for (i=0; i<Nwt; i++)
|
|
||||||
# par = par + ibits[hrows[p*Nwt+i]-1];
|
|
||||||
# // -1 as matlab arrays start from 1, C from 0
|
|
||||||
# tmp = par + prev;
|
|
||||||
# // printf(" p ind %d, parity %d \n", p, tmp);
|
|
||||||
# //c = getchar();
|
|
||||||
# tmp &= 1; // only retain the lsb
|
|
||||||
# prev = tmp;
|
|
||||||
# pbits[p] =tmp;
|
|
||||||
# }
|
|
||||||
# }
|
|
||||||
|
|
||||||
|
|
||||||
# Load Parity table upon load of this library.
|
|
||||||
hrows_2064 = np.loadtxt("ldpc_2064.txt",delimiter=',',comments='#').astype(np.int)
|
|
||||||
|
|
||||||
|
|
||||||
# Direct port of encode() above.
|
|
||||||
# Takes an input array of 0s and 1s.
|
|
||||||
# Should look at vectorising this somehow.
|
|
||||||
#@profile
|
|
||||||
def ldpc_encode(ibits,Nibits=2064,Npbits=516,Nwt=12,hrows=hrows_2064):
|
|
||||||
prev = 0
|
|
||||||
pbits = np.zeros(Npbits)
|
|
||||||
|
|
||||||
for p in xrange(Npbits):
|
|
||||||
#par = 0
|
|
||||||
#for i in xrange(Nwt):
|
|
||||||
# par = par + ibits[hrows[p*Nwt+i]-1]
|
|
||||||
par = int(np.sum(ibits[hrows[(p*Nwt+np.arange(Nwt))]-1])) # Some vectorisation of the above.
|
|
||||||
|
|
||||||
tmp = (par + prev)&1
|
|
||||||
prev = tmp
|
|
||||||
pbits[p] = tmp
|
|
||||||
|
|
||||||
return pbits
|
|
||||||
|
|
||||||
# Wrapper function for the above, allowing LDPC coding of a 258 byte long string.
|
|
||||||
def ldpc_encode_string(payload,Nibits=2064,Npbits=516,Nwt=12,hrows=hrows_2064):
|
|
||||||
if len(payload) != 258:
|
if len(payload) != 258:
|
||||||
raise TypeError("Payload MUST be 258 bytes in length! (2064 bit codeword)")
|
raise TypeError("Payload MUST be 258 bytes in length! (2064 bit codeword)")
|
||||||
|
|
||||||
# Convert to bits.
|
# Get input data into the right form (list of 0s and 1s)
|
||||||
raw_data = np.array([],dtype=np.uint8)
|
ibits = np.unpackbits(np.fromstring(payload,dtype=np.uint8)).astype(np.uint8)
|
||||||
ibits = np.unpackbits(np.fromstring(payload,dtype=np.uint8))
|
pbits = np.zeros(Npbits).astype(np.uint8)
|
||||||
|
|
||||||
parity = ldpc_encode(ibits,Nibits,Npbits,Nwt,hrows)
|
_ldpc_enc.encode(ibits, pbits)
|
||||||
|
|
||||||
return np.packbits(parity.astype(np.uint8)).tostring()
|
return np.packbits(np.array(list(pbits)).astype(np.uint8)).tostring()
|
||||||
|
|
||||||
|
|
||||||
#
|
# Some testing functions, to time encoding performance.
|
||||||
# Testing Stuff
|
|
||||||
#
|
|
||||||
|
|
||||||
def generate_dummy_packet():
|
def generate_dummy_packet():
|
||||||
payload = np.arange(0,256,1)
|
payload = np.arange(0,256,1)
|
||||||
payload = np.append(payload,[0,0]).astype(np.uint8).tostring() # Add on dummy checksum, for a total of 258 bytes.
|
payload = np.append(payload,[0,0]).astype(np.uint8).tostring() # Add on dummy checksum, for a total of 258 bytes.
|
||||||
return payload
|
return payload
|
||||||
|
|
||||||
#@profile
|
|
||||||
def main():
|
def main():
|
||||||
# Generate a dummy test packet, and convert it to an array of 0 and 1.
|
# Generate a dummy test packet, and convert it to an array of 0 and 1.
|
||||||
payload = generate_dummy_packet()
|
payload = generate_dummy_packet()
|
||||||
|
@ -97,12 +56,17 @@ def main():
|
||||||
|
|
||||||
# Now run ldpc_encode over it X times.
|
# Now run ldpc_encode over it X times.
|
||||||
parity = ""
|
parity = ""
|
||||||
for x in xrange(100):
|
start = time.time()
|
||||||
parity = ldpc_encode_string(payload)
|
for x in xrange(1000):
|
||||||
|
#print(x)
|
||||||
|
parity = ldpc_encode(payload)
|
||||||
|
|
||||||
|
stop = time.time()
|
||||||
|
print("time delta: %.3f" % (stop-start))
|
||||||
|
|
||||||
print("LDPC Parity Bits (hex): %s" % ("".join("{:02x}".format(ord(c)) for c in parity)))
|
print("LDPC Parity Bits (hex): %s" % ("".join("{:02x}".format(ord(c)) for c in parity)))
|
||||||
print("Done!")
|
print("Done!")
|
||||||
|
|
||||||
# Some basic test code.
|
# Some basic test code.
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|
12
tx_picam.py
12
tx_picam.py
|
@ -15,19 +15,19 @@ try:
|
||||||
if len(callsign)>6:
|
if len(callsign)>6:
|
||||||
callsign = callsign[:6]
|
callsign = callsign[:6]
|
||||||
except:
|
except:
|
||||||
print("Usage: python tx_picam.py CALLSIGN")
|
print("Usage: python tx_picam.py CALLSIGN ")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
print("Using callsign: %s" % callsign)
|
print("Using callsign: %s" % callsign)
|
||||||
|
|
||||||
|
fec = False
|
||||||
debug_output = False # If True, packet bits are saved to debug.bin as one char per bit.
|
debug_output = False # If True, packet bits are saved to debug.bin as one char per bit.
|
||||||
|
|
||||||
def transmit_file(filename, tx_object):
|
def transmit_file(filename, tx_object):
|
||||||
file_size = os.path.getsize(filename)
|
file_size = os.path.getsize(filename)
|
||||||
|
|
||||||
if file_size % 256 > 0:
|
if file_size % 256 > 0:
|
||||||
write_debug_message("File size not a multiple of 256 bytes!")
|
tx.set_idle_message("File size not a multiple of 256 bytes!")
|
||||||
return
|
return
|
||||||
|
|
||||||
print("Transmitting %d Packets." % (file_size/256))
|
print("Transmitting %d Packets." % (file_size/256))
|
||||||
|
@ -43,7 +43,7 @@ def transmit_file(filename, tx_object):
|
||||||
tx_object.wait()
|
tx_object.wait()
|
||||||
|
|
||||||
|
|
||||||
tx = PacketTX.PacketTX(debug=debug_output, callsign=callsign)
|
tx = PacketTX.PacketTX(debug=debug_output, callsign=callsign, fec=fec)
|
||||||
tx.start_tx()
|
tx.start_tx()
|
||||||
|
|
||||||
image_id = 0
|
image_id = 0
|
||||||
|
@ -53,10 +53,10 @@ try:
|
||||||
# Capture image using PiCam
|
# Capture image using PiCam
|
||||||
print("Capturing Image...")
|
print("Capturing Image...")
|
||||||
capture_time = datetime.datetime.utcnow().strftime("%Y%m%d-%H%M%SZ")
|
capture_time = datetime.datetime.utcnow().strftime("%Y%m%d-%H%M%SZ")
|
||||||
capture_multiple(filename="./tx_images/%s.jpg"%capture_time)
|
capture_multiple(filename="./tx_images/%s.jpg"%capture_time, debug_ptr = tx.set_idle_message)
|
||||||
#os.system("raspistill -t 100 -o ./tx_images/%s.jpg -vf -hf -w 1024 -h 768" % capture_time)
|
#os.system("raspistill -t 100 -o ./tx_images/%s.jpg -vf -hf -w 1024 -h 768" % capture_time)
|
||||||
# Resize using convert
|
# Resize using convert
|
||||||
write_debug_message("Converting Image to SSDV...")
|
tx.set_idle_message("Converting Image to SSDV...")
|
||||||
#os.system("convert temp.jpg -resize %s\! temp.jpg" % tx_resolution)
|
#os.system("convert temp.jpg -resize %s\! temp.jpg" % tx_resolution)
|
||||||
# SSDV'ify the image.
|
# SSDV'ify the image.
|
||||||
os.system("ssdv -e -n -c %s -i %d ./tx_images/%s.jpg temp.ssdv" % (callsign,image_id,capture_time))
|
os.system("ssdv -e -n -c %s -i %d ./tx_images/%s.jpg temp.ssdv" % (callsign,image_id,capture_time))
|
||||||
|
|
|
@ -0,0 +1,90 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
#
|
||||||
|
# PiCam Transmitter Script
|
||||||
|
# Capture images from the PiCam, and transmit them.
|
||||||
|
# Multiple baud rate test version.
|
||||||
|
#
|
||||||
|
# Mark Jessop <vk5qi@rfhead.net>
|
||||||
|
#
|
||||||
|
|
||||||
|
import PacketTX, sys, os, datetime, time
|
||||||
|
from PacketTX import write_debug_message
|
||||||
|
from wenet_util import *
|
||||||
|
|
||||||
|
try:
|
||||||
|
callsign = sys.argv[1]
|
||||||
|
if len(callsign)>6:
|
||||||
|
callsign = callsign[:6]
|
||||||
|
except:
|
||||||
|
print("Usage: python tx_picam.py CALLSIGN ")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
print("Using callsign: %s" % callsign)
|
||||||
|
|
||||||
|
|
||||||
|
fec = False
|
||||||
|
debug_output = False # If True, packet bits are saved to debug.bin as one char per bit.
|
||||||
|
|
||||||
|
baud_rate_1 = 115200
|
||||||
|
baud_rate_2 = 19200
|
||||||
|
|
||||||
|
def transmit_file(filename, tx_object):
|
||||||
|
file_size = os.path.getsize(filename)
|
||||||
|
|
||||||
|
if file_size % 256 > 0:
|
||||||
|
tx.set_idle_message("File size not a multiple of 256 bytes!")
|
||||||
|
return
|
||||||
|
|
||||||
|
print("Transmitting %d Packets." % (file_size/256))
|
||||||
|
|
||||||
|
f = open(filename,'rb')
|
||||||
|
|
||||||
|
for x in range(file_size/256):
|
||||||
|
data = f.read(256)
|
||||||
|
tx_object.tx_packet(data)
|
||||||
|
|
||||||
|
f.close()
|
||||||
|
print("Waiting for tx queue to empty...")
|
||||||
|
tx_object.wait()
|
||||||
|
|
||||||
|
tx = PacketTX.PacketTX(debug=debug_output, callsign=callsign,serial_baud=baud_rate_1)
|
||||||
|
tx.start_tx()
|
||||||
|
|
||||||
|
image_id = 0
|
||||||
|
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
# Capture image using PiCam
|
||||||
|
print("Capturing Image...")
|
||||||
|
capture_time = datetime.datetime.utcnow().strftime("%Y%m%d-%H%M%SZ")
|
||||||
|
capture_multiple(filename="./tx_images/%s.jpg"%capture_time, debug_ptr = tx.set_idle_message)
|
||||||
|
#os.system("raspistill -t 100 -o ./tx_images/%s.jpg -vf -hf -w 1024 -h 768" % capture_time)
|
||||||
|
# Resize using convert
|
||||||
|
tx.set_idle_message("Converting Image to SSDV...")
|
||||||
|
#os.system("convert temp.jpg -resize %s\! temp.jpg" % tx_resolution)
|
||||||
|
# SSDV'ify the image.
|
||||||
|
os.system("ssdv -e -n -c %s -i %d ./tx_images/%s.jpg temp.ssdv" % (callsign,image_id,capture_time))
|
||||||
|
# Transmit image
|
||||||
|
print("Transmitting...")
|
||||||
|
|
||||||
|
# Transmit first in 115200 baud.
|
||||||
|
transmit_file("temp.ssdv",tx)
|
||||||
|
tx.close()
|
||||||
|
time.sleep(2)
|
||||||
|
|
||||||
|
# Transmit again in 19200 baud
|
||||||
|
tx = PacketTX.PacketTX(debug=debug_output, callsign=callsign, serial_baud=baud_rate_2, fec = fec)
|
||||||
|
tx.start_tx()
|
||||||
|
transmit_file("temp.ssdv",tx)
|
||||||
|
tx.close()
|
||||||
|
time.sleep(2)
|
||||||
|
|
||||||
|
# Re-open TX in 115200 baud.
|
||||||
|
tx = PacketTX.PacketTX(debug=debug_output, callsign=callsign, serial_baud=baud_rate_1, fec = fec)
|
||||||
|
tx.start_tx()
|
||||||
|
|
||||||
|
# Increment Counter
|
||||||
|
image_id = (image_id+1)%256
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print("Closing")
|
||||||
|
tx.close()
|
|
@ -9,7 +9,7 @@
|
||||||
import PacketTX, sys, os
|
import PacketTX, sys, os
|
||||||
|
|
||||||
# Set to whatever resolution you want to test.
|
# Set to whatever resolution you want to test.
|
||||||
file_path = "./test_images/%d_320x240.ssdv" # _raw, _800x608, _640x480, _320x240
|
file_path = "./test_images/%d_raw.ssdv" # _raw, _800x608, _640x480, _320x240
|
||||||
image_numbers = xrange(1,14)
|
image_numbers = xrange(1,14)
|
||||||
|
|
||||||
debug_output = True # If True, packet bits are saved to debug.bin as one char per bit.
|
debug_output = True # If True, packet bits are saved to debug.bin as one char per bit.
|
||||||
|
@ -34,15 +34,15 @@ def transmit_file(filename, tx_object):
|
||||||
tx_object.wait()
|
tx_object.wait()
|
||||||
|
|
||||||
|
|
||||||
tx = PacketTX.PacketTX(debug=debug_output)
|
tx = PacketTX.PacketTX(debug=debug_output,fec=False)
|
||||||
tx.start_tx()
|
tx.start_tx()
|
||||||
print("TX Started. Press Ctrl-C to stop.")
|
print("TX Started. Press Ctrl-C to stop.")
|
||||||
try:
|
try:
|
||||||
while True:
|
for img in image_numbers:
|
||||||
for img in image_numbers:
|
filename = file_path % img
|
||||||
filename = file_path % img
|
print("\nTXing: %s" % filename)
|
||||||
print("\nTXing: %s" % filename)
|
transmit_file(filename,tx)
|
||||||
transmit_file(filename,tx)
|
tx.close()
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
print("Closing...")
|
print("Closing...")
|
||||||
tx.close()
|
tx.close()
|
||||||
|
|
Ładowanie…
Reference in New Issue