From 60578aa5c45a2e5897a4c8b400f0706eada20f5c Mon Sep 17 00:00:00 2001 From: Sven Steudte Date: Mon, 2 Oct 2017 15:54:28 +0200 Subject: [PATCH] Implemented Base128 and new packet type for APRS picture transmission --- decoder/base128.py | 126 +++++++++++++++++++++ decoder/base192.py | 148 +++++++++++++++++++++++++ decoder/decoder.py | 72 +++++++++--- decoder/pecan_rx_gui/ssdv | Bin 0 -> 130616 bytes tracker/software/Makefile | 1 + tracker/software/math/base128.c | 119 ++++++++++++++++++++ tracker/software/math/base128.h | 10 ++ tracker/software/protocols/aprs/aprs.c | 7 +- tracker/software/protocols/aprs/aprs.h | 6 +- tracker/software/threads/image.c | 31 +++--- 10 files changed, 485 insertions(+), 35 deletions(-) create mode 100644 decoder/base128.py create mode 100644 decoder/base192.py create mode 100755 decoder/pecan_rx_gui/ssdv create mode 100644 tracker/software/math/base128.c create mode 100644 tracker/software/math/base128.h diff --git a/decoder/base128.py b/decoder/base128.py new file mode 100644 index 0000000..b61bfac --- /dev/null +++ b/decoder/base128.py @@ -0,0 +1,126 @@ +# Copyright 2013-2016 Alex Danilo +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Base 128/192 encoding experiment. +# +# Ths is just a quick experiment to see what sort of speed we get by encoding +# a contiguous 128 value range in bytes that replaces base 64 with it's low level +# conditionals and bit-bashing for decode. +# +# Ultimately, the encoding I'd like to use is a 192 value encoding which yields +# 7.5 bits/byte - i.e. approx. 6.66% loss encoding binary data in a text-safe +# transfer form. The 128 value coding is just to test the basis of the +# speed and compressibility of the result and is easy to use in JS. The 192 +# value encoding is more efficient but likely a lot trickier to use from JS. +# +# NB: This is using ISO-9959-1 as it's basis. The source code will break if you +# try to view as UTF-8. + + +# The decode table reverses the encoding back to bits. Note, that since the most we can encode is 7.5 bits, we can never +# generate 0xFF in the decode so use that to mark 0xFF encoding bytes. + +import struct + +dtab = [ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 187, 0xFF, 188, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 189, 190, 0xFF, 0xFF, 0xFF, + 191, 0xFE, 0xFF, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 0xFF, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, + 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, + 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, + 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, + 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, + 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, + 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186 +] + +# Reserve " (0x3E) as delimiter for easy manipulation in JS, '!' as stuffing character and generation for JS - the ranges 23->7E then A0->FF create the encoding. +# That's enough for the base 128 version but we need 4 more characters to encode 7.5 bits/byte so the last set is carefully chosen from within +# the control character range using values that are safe for editors. +etab = "#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xFF\t\x0B\x1B\x1C "; + +INBYTES = 7 +OUTBYTES = 8 + +def encode(infile): + outfile = '' + k = 0 + + while k < len(infile): + _len = 0 + _in = [] + for i in range(INBYTES): + _in.append(infile[k]) + k += 1 + _len += 1 + if k >= len(infile): + break + + if _len > 0: + topbits = 0 + mask = 0x40 + for i in range(_len): + outfile += etab[_in[i] & 0x7F] + if _in[i] > 0x7F: + topbits |= mask + mask >>= 1 + + if _len < INBYTES: + outfile += '!' + + outfile += etab[topbits] + + return outfile + +def decode(infile): + out = bytearray() + k = 0 + + while k < len(infile): + _len = 0 + _in = [] + for i in range(OUTBYTES): + _in.append(ord(infile[k])) + k += 1 + _len += 1; + if k >= len(infile): + break + + if _len > 0: + topbits = dtab[_in[_len - 1]] + mask = 0x40 + _len = 0 + for i in range(len(_in)-1): + if _in[i] == 0x21: + break + _len += 1 + _in[i] = dtab[_in[i]] + if _in[i] == 0xFF: + return 0 + if topbits & mask: + _in[i] |= 0x80 + mask >>= 1 + + for i in range(_len): + out += struct.pack('B', _in[i]) + + return out + diff --git a/decoder/base192.py b/decoder/base192.py new file mode 100644 index 0000000..54f3d7c --- /dev/null +++ b/decoder/base192.py @@ -0,0 +1,148 @@ +# Copyright 2013-2016 Alex Danilo +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Base 128/192 encoding experiment. +# +# Ths is just a quick experiment to see what sort of speed we get by encoding +# a contiguous 128 value range in bytes that replaces base 64 with it's low level +# conditionals and bit-bashing for decode. +# +# Ultimately, the encoding I'd like to use is a 192 value encoding which yields +# 7.5 bits/byte - i.e. approx. 6.66% loss encoding binary data in a text-safe +# transfer form. The 128 value coding is just to test the basis of the +# speed and compressibility of the result and is easy to use in JS. The 192 +# value encoding is more efficient but likely a lot trickier to use from JS. +# +# NB: This is using ISO-9959-1 as it's basis. The source code will break if you +# try to view as UTF-8. + + +# The decode table reverses the encoding back to bits. Note, that since the most we can encode is 7.5 bits, we can never +# generate 0xFF in the decode so use that to mark 0xFF encoding bytes. +dtab = [ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 187, 0xFF, 188, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 189, 190, 0xFF, 0xFF, 0xFF, + 191, 0xFE, 0xFF, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 0xFF, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, + 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, + 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, + 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, + 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, + 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, + 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186 +] + +# Reserve " (0x3E) as delimiter for easy manipulation in JS, '!' as stuffing character and generation for JS - the ranges 23->7E then A0->FF create the encoding. +# That's enough for the base 128 version but we need 4 more characters to encode 7.5 bits/byte so the last set is carefully chosen from within +# the control character range using values that are safe for editors. +etab = "#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xFF\t\x0B\x1B\x1C "; + +BIGINBYTES = 15 +BIGOUTBYTES = 16 + +# Perform 120 bit binary number conversion into base 192 packed as bytes. +def base192_encode(_in): + out = [0]*BIGOUTBYTES + for i in range(BIGOUTBYTES): + val = _in % 192 + out[i] = val & 0xFF + _in /= 192 + return out,_in + +# Perform base 192 into binary conversion. +def base192_decode(_in): + out = 0 + i = BIGOUTBYTES - 1 + while i >= 0: + out *= 192 + out += _in[i] + i -= 1 + return out,_in + +# Encode 7.5 bits per output byte. +# +# To do this means doing a base conversion into base 192 then encode the +# digits one per byte using the encoding table. We convert 15 bytes to +# 16 this way. In order to achieve that we need to do division on +# a 120 bit number (the 15 bytes of input) to generate the base 192 digits. +# +# NB: This quick test program is assuming little-endian byte order so we +# can use the inbuilt 128 bit types for the division. For portability +# we should really use something like Knuth's arbitrary precision +# division algorithms. +def b192_encode(infile): + #int i, len, 0xFE; + value = 0 + k = 0 + outfile = '' + + while k < len(infile): + _len = 0 + for i in range(BIGINBYTES): + value += ord(infile[k]) << (i*8) + k += 1 + _len += 1 + if k == len(infile): + break + + if _len > 0: + + out,value = base192_encode(value) + _len += 1; # Always outputs 1 byte more than the input + for i in range(_len): + outfile += etab[out[i]] + + if BIGINBYTES-i > 0: # Need to mark early end with the pad character + outfile += '!' + + return outfile + +def b192_decode(infile): + value = 0 + j = 0 + outfile = [] + + while j+1 < len(infile): + _len = pad = 0 + i = -1 + _in = [] + for i in range(BIGOUTBYTES): + + if j+1 == len(infile): # Shouldn't happen for valid 0xFEded content + break + + _in.append(dtab[ord(infile[j])]) + j += 1 + + if _in[i] == 0xFF: # 0xFF character in the encoding set + return 0 + + _len += 1 + + if _len > 0: + while len(_in) < BIGOUTBYTES: # Fill MSBs with 0 - NB up to 128 bits, top 8 never used + _in.append(0) + value,_in = base192_decode(_in) + _len -= 1 # Output always shrinks by one byte + for i in range(_len): + outfile.append((value >> (i*8)) & 0xFF) + + return outfile + diff --git a/decoder/decoder.py b/decoder/decoder.py index 9f3cba6..c4426a9 100755 --- a/decoder/decoder.py +++ b/decoder/decoder.py @@ -2,7 +2,7 @@ import serial,os,re,datetime from subprocess import call -import base91 +import base128 import binascii import urllib2 import io @@ -45,8 +45,6 @@ elif args.device is not '-': # Use serial connection (probably TNC) sys.stderr.write('Error: Could not open serial port\n') sys.exit(1) - ser = io.TextIOWrapper(io.BufferedRWPair(serr, serr, 1), newline = '\r', line_buffering = True) # Define Newline as \r - # Open logging file if args.log is not None: @@ -60,25 +58,37 @@ jsons = [] def decode_callsign(code): callsign = '' - + while code > 0: s = code % 40 - if s == 0: callsign += '-' + if s == 0: callsign += '-' elif s < 11: callsign += chr(47 + s) elif s < 14: callsign += '-' else: callsign += chr(51 + s) code /= 40 - + return callsign +def encode_callsign(callsign): + x = 0 + for i in range(len(callsign)-1,-1,-1): + x *= 40 + c = ord(callsign[i]) + if c >= 65 and c <= 90: x += c - 51 + elif c >= 97 and c <= 122: x += c - 83 + elif c >= 48 and c <= 57: x += c - 47 + + return x + + + def received_data(data): global jsons - # Parse line and detect data - m = re.search("(.*)\>APECAN(.*):\{\{I(.*)", data) + m = re.search("(.*)\>APECAN(.*):\!(.*)", data) try: - call = m.group(1) - aprs = m.group(3) + call = m.group(1).split(' ')[-1] # transmitter callsign + data128 = m.group(3) # base128 encoded SSDV data (without SYNC, PacketType, Callsign, CRC, FEC) if len(m.group(2)) > 0: receiver = m.group(2).split(',')[-1] else: @@ -89,11 +99,24 @@ def received_data(data): if args.log is not None: f.write(data) # Log data to file - data = base91.decode(aprs) # Decode Base91 + data = base128.decode(data128) # Decode Base128 - if len(data) != 219: + if len(data) != 214: return # APRS message sampled too short + # Encode callsign (ensure callsign has no more than 6 chars) + bcall = call.split('-') # Split callsign and SSID + if len(bcall) == 1: # No SSID available, so take the callsign + bcall = bcall[0][0:6] + elif(len(bcall[0]) < 5): # Callsign has 4 chars, so take it with the SSID + bcall = bcall[0] + bcall[1][0:2] + elif(len(bcall[0]) < 6): # Callsign has 5 chars, so take it with the last digit of the SSID + bcall = bcall[0] + bcall[1][-1] + else: + bcall = bcall[0][0:6] # Callsign has 6 chars, so take the call without SSID + + data = binascii.unhexlify('66%08x' % encode_callsign(bcall)) + data + # Calculate CRC for SSDV server crc = binascii.crc32(data) & 0xffffffff @@ -107,7 +130,6 @@ def received_data(data): \"receiver\": \"""" + receiver + """\" }""") - call = decode_callsign(0x1000000*data[1] + 0x10000*data[2] + 0x100*data[3] + data[4]) print datetime.datetime.now().isoformat('T') + ' Received packet call %s image %d packet %d' % (call, data[5], 0x100*data[6]+data[7]) if len(jsons) >= args.grouping: # Enough packets collected, send them all to the server @@ -177,6 +199,28 @@ if args.device == 'I': # APRS-IS else: # stdin or serial while True: - data = sys.stdin.readline() if args.device is '-' else ser.readline() # Read a line + # Read data + if args.device is '-': + data = sys.stdin.readline() + else: + data = '' + while True: + b = serr.read(1) + if b == '\r' or b == '\n': + break + data += b + received_data(data) + + + + + + + + + + + + diff --git a/decoder/pecan_rx_gui/ssdv b/decoder/pecan_rx_gui/ssdv new file mode 100755 index 0000000000000000000000000000000000000000..716d76e30244f91dd2c3a7f26e6c5309daa9f612 GIT binary patch literal 130616 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

iAHnCUKd#P2J07i` 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

RLXfN97amFd- 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 QeWZSV@Hus({ZKUj1+&52lmGw# literal 0 HcmV?d00001 diff --git a/tracker/software/Makefile b/tracker/software/Makefile index 79c5272..96d2947 100644 --- a/tracker/software/Makefile +++ b/tracker/software/Makefile @@ -144,6 +144,7 @@ CSRC = $(STARTUPSRC) \ sleep.c \ threads/threads.c \ math/base.c \ + math/base128.c \ math/sgp4.c \ math/geofence.c \ config.c \ diff --git a/tracker/software/math/base128.c b/tracker/software/math/base128.c new file mode 100644 index 0000000..ab0d73e --- /dev/null +++ b/tracker/software/math/base128.c @@ -0,0 +1,119 @@ +#include + +#define PADCHAR '!' +#define INBYTES 7 +#define OUTBYTES 8 + +#define ZOF_TAB 256 +#define BAD 0xFF +#define PAD 0xFE /* Encoded '!' is padding */ + +static const unsigned char dtab[ZOF_TAB] = +{ + BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, 187, BAD, 188, BAD, BAD, BAD, BAD, + BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, 189, 190, BAD, BAD, BAD, + 191, PAD, BAD, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, BAD, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, + 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, BAD, + BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, + BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, + 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, + 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, + 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, + 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, + 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, + 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, +}; +static const char *etab = "#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~" + "\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF" + "\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF" + "\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xFF" + "\t\x0B\x1B\x1C "; + +uint32_t b128_encode(uint8_t *infile, uint8_t *outfile, uint32_t input_length) +{ + unsigned char in[INBYTES]; + unsigned char topbits, mask; + int i, len; + uint32_t k = 0; + uint32_t j = 0; + + while(k < input_length) + { + len = 0; + for (i = 0; i < INBYTES; i++) + { + in[i] = infile[k++]; + len++; + if (k >= input_length) { + break; + } + } + if (len > 0) + { + topbits = 0; + mask = 0x40; + for (i = 0; i < len; i++) + { + outfile[j++] = etab[in[i] & 0x7F]; + if (in[i] > 0x7F) + topbits |= mask; + mask >>= 1; + } + if (len < INBYTES) /* Need to pad */ + { + outfile[j++] = PADCHAR; + } + outfile[j++] = etab[topbits]; + } + } + outfile[j] = 0; + return j; +} + +uint32_t b128_decode(uint8_t *infile, uint8_t *outfile, uint32_t input_length) +{ + unsigned char in[OUTBYTES]; + unsigned char topbits, mask; + int i, len; + uint32_t k = 0; + uint32_t j = 0; + + while(k < input_length) + { + len = 0; + for (i = 0; i < OUTBYTES; i++) + { + in[i] = infile[k++]; + len++; + if (k >= input_length) + break; + } + if (len > 0) + { + topbits = dtab[in[len - 1]]; + mask = 0x40; + for (len = i = 0; i < INBYTES; i++) + { + if (in[i] == PADCHAR) + break; + len++; + in[i] = dtab[in[i]]; + if (in[i] == BAD) /* Illegal encoding character, bail out */ + return 0; + if (topbits & mask) + in[i] |= 0x80; + mask >>= 1; + } + for (i = 0; i < len; i++) + { + outfile[j++] = in[i]; + } + } + } + return j; +} + diff --git a/tracker/software/math/base128.h b/tracker/software/math/base128.h new file mode 100644 index 0000000..3b237e6 --- /dev/null +++ b/tracker/software/math/base128.h @@ -0,0 +1,10 @@ +#ifndef __BASE128_H__ +#define __BASE128_H__ + +#include + +uint32_t b128_encode(uint8_t *infile, uint8_t *outfile, uint32_t input_length); +uint32_t b128_decode(uint8_t *infile, uint8_t *outfile, uint32_t input_length); + +#endif + diff --git a/tracker/software/protocols/aprs/aprs.c b/tracker/software/protocols/aprs/aprs.c index be59c74..b388ab8 100644 --- a/tracker/software/protocols/aprs/aprs.c +++ b/tracker/software/protocols/aprs/aprs.c @@ -203,7 +203,7 @@ uint32_t aprs_encode_experimental(char packetType, uint8_t* message, mod_t mod, /** * Transmit custom data packet (the methods aprs_encode_data allow multiple APRS packets in a row without preable being sent) */ -void aprs_encode_data_init(ax25_t* packet, uint8_t* message, mod_t mod) +void aprs_encode_packet_init(ax25_t* packet, uint8_t* message, mod_t mod) { packet->data = message; packet->max_size = 8192; // TODO: replace 8192 with real size @@ -212,11 +212,10 @@ void aprs_encode_data_init(ax25_t* packet, uint8_t* message, mod_t mod) // Encode APRS header ax25_init(packet); } -uint32_t aprs_encode_data_encodePacket(ax25_t* packet, char packetType, const aprs_conf_t *config, uint8_t *data, size_t size) +uint32_t aprs_encode_packet_encodePacket(ax25_t* packet, char packetType, const aprs_conf_t *config, uint8_t *data, size_t size) { // Encode header ax25_send_header(packet, config->callsign, config->ssid, config->path, packet->size > 0 ? 0 : config->preamble); - ax25_send_string(packet, "{{"); ax25_send_byte(packet, packetType); // Encode message @@ -228,7 +227,7 @@ uint32_t aprs_encode_data_encodePacket(ax25_t* packet, char packetType, const ap return packet->size; } -uint32_t aprs_encode_data_finalize(ax25_t* packet) +uint32_t aprs_encode_packet_finalize(ax25_t* packet) { scramble(packet); nrzi_encode(packet); diff --git a/tracker/software/protocols/aprs/aprs.h b/tracker/software/protocols/aprs/aprs.h index c6ff7ec..09404b6 100644 --- a/tracker/software/protocols/aprs/aprs.h +++ b/tracker/software/protocols/aprs/aprs.h @@ -54,9 +54,9 @@ uint32_t aprs_encode_telemetry_configuration(uint8_t* message, mod_t mod, const uint32_t aprs_encode_message(uint8_t* message, mod_t mod, const aprs_conf_t *config, const char *receiver, const char *text); uint32_t aprs_encode_experimental(char packetType, uint8_t* message, mod_t mod, const aprs_conf_t *config, uint8_t *data, size_t size); -void aprs_encode_data_init(ax25_t* packet, uint8_t* message, mod_t mod); -uint32_t aprs_encode_data_encodePacket(ax25_t* packet, char packetType, const aprs_conf_t *config, uint8_t *data, size_t size); -uint32_t aprs_encode_data_finalize(ax25_t* packet); +void aprs_encode_packet_init(ax25_t* packet, uint8_t* message, mod_t mod); +uint32_t aprs_encode_packet_encodePacket(ax25_t* packet, char packetType, const aprs_conf_t *config, uint8_t *data, size_t size); +uint32_t aprs_encode_packet_finalize(ax25_t* packet); #endif diff --git a/tracker/software/threads/image.c b/tracker/software/threads/image.c index 4e863ef..bd59bae 100644 --- a/tracker/software/threads/image.c +++ b/tracker/software/threads/image.c @@ -8,7 +8,7 @@ #include "ssdv.h" #include "aprs.h" #include "radio.h" -#include "base.h" +#include "base128.h" #include #include "types.h" #include "sleep.h" @@ -273,6 +273,8 @@ const uint8_t noCameraFound[4071] = { 0xBD, 0xC0, 0x20, 0x00, 0x01, 0xFF, 0xD9 }; +#include + uint8_t gimage_id; // Global image ID (for all image threads) mutex_t camera_mtx; bool camera_mtx_init = false; @@ -281,7 +283,7 @@ void encode_ssdv(const uint8_t *image, uint32_t image_len, module_conf_t* conf, { ssdv_t ssdv; uint8_t pkt[SSDV_PKT_SIZE]; - uint8_t pkt_base91[BASE91LEN(SSDV_PKT_SIZE-37)]; + uint8_t pkt_base128[256]; const uint8_t *b; uint32_t bi = 0; uint8_t c = SSDV_OK; @@ -304,7 +306,7 @@ void encode_ssdv(const uint8_t *image, uint32_t image_len, module_conf_t* conf, msg.mod = conf->protocol == PROT_APRS_AFSK ? MOD_AFSK : MOD_2GFSK; msg.afsk_conf = &(conf->afsk_conf); msg.gfsk_conf = &(conf->gfsk_conf); - aprs_encode_data_init(&ax25_handle, msg.msg, msg.mod); + aprs_encode_packet_init(&ax25_handle, msg.msg, msg.mod); } while(true) @@ -320,11 +322,11 @@ void encode_ssdv(const uint8_t *image, uint32_t image_len, module_conf_t* conf, if(r <= 0) { TRACE_ERROR("SSDV > Premature end of file"); - if(conf->protocol == PROT_APRS_2GFSK || conf->protocol == PROT_APRS_AFSK) msg.bin_len = aprs_encode_data_finalize(&ax25_handle); + if(conf->protocol == PROT_APRS_2GFSK || conf->protocol == PROT_APRS_AFSK) msg.bin_len = aprs_encode_packet_finalize(&ax25_handle); if(msg.bin_len > 0) transmitOnRadio(&msg, false); // Empty buffer if(conf->protocol == PROT_APRS_2GFSK || conf->protocol == PROT_APRS_AFSK) { - aprs_encode_data_init(&ax25_handle, msg.msg, msg.mod); + aprs_encode_packet_init(&ax25_handle, msg.msg, msg.mod); msg.bin_len = 0; } break; @@ -335,21 +337,21 @@ void encode_ssdv(const uint8_t *image, uint32_t image_len, module_conf_t* conf, if(c == SSDV_EOI) { TRACE_INFO("SSDV > ssdv_enc_get_packet said EOI"); - if(conf->protocol == PROT_APRS_2GFSK || conf->protocol == PROT_APRS_AFSK) msg.bin_len = aprs_encode_data_finalize(&ax25_handle); + if(conf->protocol == PROT_APRS_2GFSK || conf->protocol == PROT_APRS_AFSK) msg.bin_len = aprs_encode_packet_finalize(&ax25_handle); if(msg.bin_len > 0) transmitOnRadio(&msg, false); // Empty buffer if(conf->protocol == PROT_APRS_2GFSK || conf->protocol == PROT_APRS_AFSK) { - aprs_encode_data_init(&ax25_handle, msg.msg, msg.mod); + aprs_encode_packet_init(&ax25_handle, msg.msg, msg.mod); msg.bin_len = 0; } break; } else if(c != SSDV_OK) { TRACE_ERROR("SSDV > ssdv_enc_get_packet failed: %i", c); - if(conf->protocol == PROT_APRS_2GFSK || conf->protocol == PROT_APRS_AFSK) msg.bin_len = aprs_encode_data_finalize(&ax25_handle); + if(conf->protocol == PROT_APRS_2GFSK || conf->protocol == PROT_APRS_AFSK) msg.bin_len = aprs_encode_packet_finalize(&ax25_handle); if(msg.bin_len > 0) transmitOnRadio(&msg, false); // Empty buffer if(conf->protocol == PROT_APRS_2GFSK || conf->protocol == PROT_APRS_AFSK) { - aprs_encode_data_init(&ax25_handle, msg.msg, msg.mod); + aprs_encode_packet_init(&ax25_handle, msg.msg, msg.mod); msg.bin_len = 0; } return; @@ -360,20 +362,21 @@ void encode_ssdv(const uint8_t *image, uint32_t image_len, module_conf_t* conf, case PROT_APRS_AFSK: // Encode packet TRACE_INFO("IMG > Encode APRS/SSDV packet"); - base91_encode(&pkt[1], pkt_base91, sizeof(pkt)-37); // Sync byte, CRC and FEC of SSDV not transmitted - msg.bin_len = aprs_encode_data_encodePacket(&ax25_handle, 'I', &conf->aprs_conf, pkt_base91, strlen((char*)pkt_base91)); + b128_encode(&pkt[6], pkt_base128, sizeof(pkt)-42); // Sync byte, CRC and FEC of SSDV not transmitted (because its not neccessary inside an APRS packet) + + msg.bin_len = aprs_encode_packet_encodePacket(&ax25_handle, '!', &conf->aprs_conf, pkt_base128, strlen((char*)pkt_base128)); if(redudantTx) - msg.bin_len = aprs_encode_data_encodePacket(&ax25_handle, 'I', &conf->aprs_conf, pkt_base91, strlen((char*)pkt_base91)); + msg.bin_len = aprs_encode_packet_encodePacket(&ax25_handle, '!', &conf->aprs_conf, pkt_base128, strlen((char*)pkt_base128)); // Transmit if(msg.bin_len >= 58000 || conf->packet_spacing) // Transmit if buffer is almost full or if single packet transmission is activated (packet_spacing != 0) { // Transmit packets - msg.bin_len = aprs_encode_data_finalize(&ax25_handle); + msg.bin_len = aprs_encode_packet_finalize(&ax25_handle); transmitOnRadio(&msg, false); // Initialize new packet buffer - aprs_encode_data_init(&ax25_handle, msg.msg, msg.mod); + aprs_encode_packet_init(&ax25_handle, msg.msg, msg.mod); msg.bin_len = 0; } break;