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)
|
||||
# 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>
|
||||
#
|
||||
|
||||
|
@ -34,9 +41,9 @@ class BinaryDebug(object):
|
|||
self.f.close()
|
||||
|
||||
def write_debug_message(message, debug_file = "tx_idle_message.txt"):
|
||||
f = open(debug_file,'w')
|
||||
f.write(message)
|
||||
f.close()
|
||||
#f = open(debug_file,'w')
|
||||
#f.write(message)
|
||||
#f.close()
|
||||
print("DEBUG MSG: %s" % message)
|
||||
|
||||
class PacketTX(object):
|
||||
|
@ -59,8 +66,9 @@ class PacketTX(object):
|
|||
self.payload_length = payload_length
|
||||
|
||||
self.crc16 = crcmod.predefined.mkCrcFun('crc-ccitt-false')
|
||||
self.fec = fec
|
||||
self.callsign = callsign
|
||||
self.idle_message = "DE %s" % callsign
|
||||
self.fec = fec
|
||||
|
||||
def start_tx(self):
|
||||
self.transmit_active = True
|
||||
|
@ -82,18 +90,17 @@ class PacketTX(object):
|
|||
else:
|
||||
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.
|
||||
# This might be a useful way of getting error messages down from the payload.
|
||||
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
|
||||
return "\x00" + idle_data
|
||||
return "\x00" + "DE %s: \t%s" % (self.callsign,self.idle_message)
|
||||
|
||||
|
||||
def tx_thread(self):
|
||||
|
@ -104,7 +111,7 @@ class PacketTX(object):
|
|||
else:
|
||||
if not self.debug:
|
||||
#self.s.write(self.idle_sequence)
|
||||
self.s.write(self.frame_packet(self.generate_idle_message()))
|
||||
self.s.write(self.idle_message)
|
||||
else:
|
||||
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
|
||||
# Author: Mark Jessop <vk5qi@rfhead.net>
|
||||
# LDPC Encoder Functions.
|
||||
# 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 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
|
||||
# #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):
|
||||
def ldpc_encode_string(payload, Nibits = 2064, Npbits = 516):
|
||||
if len(payload) != 258:
|
||||
raise TypeError("Payload MUST be 258 bytes in length! (2064 bit codeword)")
|
||||
|
||||
# Convert to bits.
|
||||
raw_data = np.array([],dtype=np.uint8)
|
||||
ibits = np.unpackbits(np.fromstring(payload,dtype=np.uint8))
|
||||
# Get input data into the right form (list of 0s and 1s)
|
||||
ibits = np.unpackbits(np.fromstring(payload,dtype=np.uint8)).astype(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()
|
||||
|
||||
|
||||
#
|
||||
# Testing Stuff
|
||||
#
|
||||
# Some testing functions, to time encoding performance.
|
||||
|
||||
def generate_dummy_packet():
|
||||
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.
|
||||
return payload
|
||||
|
||||
#@profile
|
||||
def main():
|
||||
# Generate a dummy test packet, and convert it to an array of 0 and 1.
|
||||
payload = generate_dummy_packet()
|
||||
|
@ -97,12 +56,17 @@ def main():
|
|||
|
||||
# Now run ldpc_encode over it X times.
|
||||
parity = ""
|
||||
for x in xrange(100):
|
||||
parity = ldpc_encode_string(payload)
|
||||
start = time.time()
|
||||
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("Done!")
|
||||
|
||||
# Some basic test code.
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
main()
|
||||
|
|
12
tx_picam.py
12
tx_picam.py
|
@ -15,19 +15,19 @@ try:
|
|||
if len(callsign)>6:
|
||||
callsign = callsign[:6]
|
||||
except:
|
||||
print("Usage: python tx_picam.py CALLSIGN")
|
||||
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.
|
||||
|
||||
def transmit_file(filename, tx_object):
|
||||
file_size = os.path.getsize(filename)
|
||||
|
||||
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
|
||||
|
||||
print("Transmitting %d Packets." % (file_size/256))
|
||||
|
@ -43,7 +43,7 @@ def transmit_file(filename, tx_object):
|
|||
tx_object.wait()
|
||||
|
||||
|
||||
tx = PacketTX.PacketTX(debug=debug_output, callsign=callsign)
|
||||
tx = PacketTX.PacketTX(debug=debug_output, callsign=callsign, fec=fec)
|
||||
tx.start_tx()
|
||||
|
||||
image_id = 0
|
||||
|
@ -53,10 +53,10 @@ try:
|
|||
# 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)
|
||||
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
|
||||
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)
|
||||
# SSDV'ify the image.
|
||||
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
|
||||
|
||||
# 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)
|
||||
|
||||
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 = PacketTX.PacketTX(debug=debug_output)
|
||||
tx = PacketTX.PacketTX(debug=debug_output,fec=False)
|
||||
tx.start_tx()
|
||||
print("TX Started. Press Ctrl-C to stop.")
|
||||
try:
|
||||
while True:
|
||||
for img in image_numbers:
|
||||
filename = file_path % img
|
||||
print("\nTXing: %s" % filename)
|
||||
transmit_file(filename,tx)
|
||||
for img in image_numbers:
|
||||
filename = file_path % img
|
||||
print("\nTXing: %s" % filename)
|
||||
transmit_file(filename,tx)
|
||||
tx.close()
|
||||
except KeyboardInterrupt:
|
||||
print("Closing...")
|
||||
tx.close()
|
||||
|
|
Ładowanie…
Reference in New Issue