IP400/Node Firmware/IP400/Src/callsign.c
2025-03-28 12:54:00 -06:00

195 lines
4.6 KiB
C

/*---------------------------------------------------------------------------
Project: IP400
Module: Compress and expand a callsign
File Name: callsign.c
Author: MartinA
Creation Date: Jan 9, 2025
Description: Comresses an ASCII string of callsign characters. A callsign can
have up to 6 characters to fit into the four byte field, if longer
the rest are placed in the payload of the data frame. Callsigns can
be extended with a '-' character, in this case the call is removed
and padded before conversion, and the remainder is also in the
frame payload.
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 2 of the License, or
(at your option) any later version, provided this copyright notice
is included.
Copyright (c) Alberta Digital Radio Communications Society
All rights reserved.
Revision History:
---------------------------------------------------------------------------*/
#include <string.h>
#include "types.h"
#include "frame.h"
#define RADIX_40 40 // alphabet radix
// Radix 40 callsign alphabet
char alphabet[RADIX_40] = {
// 0 1 2 3 4 5 6 7 8 9
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
// 10 11 12 13 14 15 16 17 18 19
' ', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I',
// 20 21 22 23 24 25 26 27 28 29
'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
// 30 31 32 33 34 35 36 37 38 39
'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '(', ')', '-'
};
// encode a char into the alphabet
uint32_t alphaEncode(char byte)
{
byte = islower(byte) ? toupper(byte) : byte;
for(uint32_t i=0;i<RADIX_40;i++) {
if(alphabet[i] == byte)
return i;
}
return 0;
}
// decode a byte back into ASCII
char alphaDecode(uint32_t alpha)
{
// numeric
if((alpha >= 0) && (alpha <= 9))
return '0' + alpha;
// special cases: see alphabet table
switch(alpha) {
case 10:
return ' ';
case 37:
return '(';
case 38:
return ')';
case 39:
return '@';
default:
return 'A' + (alpha - 11);
}
return ' ';
}
void EncodeChunk(char *src, int len, uint32_t *enc)
{
uint32_t chunk=alphaEncode(src[0]);
// less than 2 characters
if(len < 2) {
*enc = chunk;
return;
}
// 2 or more
for(int i=1;i<len;i++) {
uint32_t current = alphaEncode(src[i]);
chunk = current + chunk*RADIX_40;
}
*enc = chunk;
}
// encode a callsign: if less than max_call, then just encode into the frame,
// else continue in the data portion of the frame
uint8_t callEncode(char *callsign, uint16_t port, IP400_FRAME *frame, uint8_t dest, uint8_t offset)
{
int len = strlen(callsign);
uint32_t encChunk;
uint32_t *p = (uint32_t *)frame->buf;
p += offset;
if(dest == DEST_CALLSIGN)
frame->dest.port = port;
else
frame->source.port = port;
// broadcast address
if(!strcmp(callsign, "FFFF")) {
if(dest == DEST_CALLSIGN)
frame->dest.callbytes.encoded = 0xFFFFFFFF;
else
frame->source.callbytes.encoded = 0xFFFFFFFF;
return 0;
}
// ensure the callsign is padded out to at least 6 characters
char paddedCall[50];
strcpy(paddedCall, callsign);
strcat(paddedCall, " ");
// non-broadcast: break it up into chunks of 6 characters
int nChunks = len/MAX_CALL;
if ((len % MAX_CALL) > 0)
nChunks++;
char *cll = paddedCall;
for(int k=0;k<nChunks;k++) {
EncodeChunk(cll, MAX_CALL, &encChunk);
if(k==0) {
if(dest == DEST_CALLSIGN)
frame->dest.callbytes.encoded = encChunk;
else
frame->source.callbytes.encoded = encChunk;
// callsign less than or equal to MAX_CALL
if(nChunks == 1) {
return 0;
}
} else {
*p++ = encChunk;
cll += MAX_CALL;
if(dest == DEST_CALLSIGN)
frame->flagfld.flags.destExt = TRUE;
else
frame->flagfld.flags.srcExt = TRUE;
}
}
*p++ = 0xFF000000;
// return the offset in the buffer
return (p-(uint32_t *)frame->buf) + sizeof(uint32_t);
}
// decode a callsign
BOOL callDecode(IP400_CALL *encCall, char *callsign, uint16_t *port)
{
char tmpBuf[10], *p = tmpBuf;
int i;
uint32_t encoded = encCall->callbytes.encoded;
for(i=0;i<MAX_CALL;i++) {
*p++ = alphaDecode(encoded % RADIX_40);
encoded /= RADIX_40;
}
*p = '\0';
for(i=strlen(tmpBuf)-1;i>=0;i--)
*callsign++ = tmpBuf[i];
*callsign = '\0';
*port = encCall->port;
return TRUE;
}