HAM-64 address format implementation
This commit is contained in:
parent
b6f040ebc7
commit
0fc63f5f69
180
impl/src/layer2/ham64.c
Normal file
180
impl/src/layer2/ham64.c
Normal file
|
@ -0,0 +1,180 @@
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "ham64.h"
|
||||||
|
|
||||||
|
static uint16_t char_to_ham64(char c)
|
||||||
|
{
|
||||||
|
if(c == 0) {
|
||||||
|
return 0;
|
||||||
|
} else if(c >= 'A' && c <= 'Z') {
|
||||||
|
return 1 + c - 'A';
|
||||||
|
} else if(c >= 'a' && c <= 'z') {
|
||||||
|
return 1 + c - 'a';
|
||||||
|
} else if(c >= '0' && c <= '9') {
|
||||||
|
return 27 + c - '0';
|
||||||
|
} else if(c == '/') {
|
||||||
|
return 37;
|
||||||
|
} else if(c == '-') {
|
||||||
|
return 38;
|
||||||
|
} else if(c == '^') {
|
||||||
|
return 39;
|
||||||
|
} else {
|
||||||
|
return 0xFFFF; // undefined character
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static char ham64_to_char(uint16_t v) {
|
||||||
|
if(v == 0) {
|
||||||
|
return 0;
|
||||||
|
} else if(v >= 1 && v <= 26) {
|
||||||
|
return 'A' + v - 1;
|
||||||
|
} else if(v >= 27 && v <= 36) {
|
||||||
|
return '0' + v - 27;
|
||||||
|
} else if(v == 37) {
|
||||||
|
return '/';
|
||||||
|
} else if(v == 38) {
|
||||||
|
return '-';
|
||||||
|
} else if(v == 39) {
|
||||||
|
return '^';
|
||||||
|
} else {
|
||||||
|
return '?';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
size_t ham64_encode(const char *call, ham64_t *ham64)
|
||||||
|
{
|
||||||
|
size_t chars_in_tmp = 0;
|
||||||
|
size_t ham64_len = 0;
|
||||||
|
|
||||||
|
uint16_t *tmp = ham64->addr;
|
||||||
|
|
||||||
|
memset(ham64->addr, 0, 4*sizeof(uint16_t));
|
||||||
|
|
||||||
|
while(*call) {
|
||||||
|
uint16_t encoded = char_to_ham64(*call);
|
||||||
|
if(encoded == 0xFFFF) {
|
||||||
|
// drop non-encodable characters
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
*tmp *= 40;
|
||||||
|
*tmp += encoded;
|
||||||
|
chars_in_tmp++;
|
||||||
|
|
||||||
|
if(chars_in_tmp == 3) {
|
||||||
|
ham64_len++;
|
||||||
|
tmp++;
|
||||||
|
|
||||||
|
chars_in_tmp = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
call++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// finalize a partially filled word
|
||||||
|
if(chars_in_tmp != 0) {
|
||||||
|
while(chars_in_tmp < 3) {
|
||||||
|
*tmp *= 40;
|
||||||
|
chars_in_tmp++;
|
||||||
|
}
|
||||||
|
|
||||||
|
ham64_len++;
|
||||||
|
}
|
||||||
|
|
||||||
|
ham64->length = ham64_len;
|
||||||
|
return ham64_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
size_t ham64_decode_callsign(const ham64_t *ham64, char *call)
|
||||||
|
{
|
||||||
|
if(ham64->length > 4 || ham64->length == 0) {
|
||||||
|
// unsupported length
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ham64->addr[0] < 0x0640 || ham64->addr[0] >= 0xFA00) {
|
||||||
|
// special address, must not be decoded here
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t call_len = 0;
|
||||||
|
|
||||||
|
for(size_t i = 0; i < ham64->length; i++) {
|
||||||
|
// big endian decoding
|
||||||
|
uint16_t chunk = ham64->addr[i];
|
||||||
|
|
||||||
|
if(chunk < 0x0640 || chunk >= 0xFA00) {
|
||||||
|
// invalid word
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(uint16_t div = 1600; div != 0; div /= 40) {
|
||||||
|
*call = ham64_to_char((chunk / div) % 40);
|
||||||
|
if(!*call) {
|
||||||
|
return call_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
call_len++;
|
||||||
|
call++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*call = '\0';
|
||||||
|
return call_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const char *ham64_addr_type_to_string(ham64_addr_type_t addr_type)
|
||||||
|
{
|
||||||
|
static const char *type_strings[HAM64_NUM_ADDR_TYPES] = {
|
||||||
|
"EMPTY",
|
||||||
|
"TMP_SHORT",
|
||||||
|
"CALLSIGN",
|
||||||
|
"BROADCAST",
|
||||||
|
"IPV6_MULTICAST",
|
||||||
|
"IPV4_MULTICAST",
|
||||||
|
"RESERVED"};
|
||||||
|
|
||||||
|
if(addr_type >= HAM64_NUM_ADDR_TYPES) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return type_strings[addr_type];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ham64_addr_type_t ham64_get_addr_type(const ham64_t *ham64)
|
||||||
|
{
|
||||||
|
uint16_t first_word = ham64->addr[0];
|
||||||
|
uint8_t first_byte = first_word >> 8;
|
||||||
|
|
||||||
|
if(first_word >= 0x0640 && first_word < 0xFA00) {
|
||||||
|
return HAM64_ADDR_TYPE_CALLSIGN;
|
||||||
|
} else if(first_word == 0x0000) {
|
||||||
|
return HAM64_ADDR_TYPE_EMPTY;
|
||||||
|
} else if(first_word < 0x0640) {
|
||||||
|
return HAM64_ADDR_TYPE_TMP_SHORT;
|
||||||
|
} else if(first_word == 0xFFFF) {
|
||||||
|
return HAM64_ADDR_TYPE_BROADCAST;
|
||||||
|
} else if(first_byte == 0xFA) {
|
||||||
|
return HAM64_ADDR_TYPE_IPV6_MULTICAST;
|
||||||
|
} else if(first_byte == 0xFB) {
|
||||||
|
return HAM64_ADDR_TYPE_IPV4_MULTICAST;
|
||||||
|
} else {
|
||||||
|
return HAM64_ADDR_TYPE_RESERVED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ham64_format(const ham64_t *ham64, char *out)
|
||||||
|
{
|
||||||
|
for(uint8_t i = 0; i < ham64->length; i++) {
|
||||||
|
sprintf(out + 5 * i, "%04X-", ham64->addr[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
out[5*ham64->length - 1] = '\0';
|
||||||
|
}
|
59
impl/src/layer2/ham64.h
Normal file
59
impl/src/layer2/ham64.h
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
#ifndef HAM64_H
|
||||||
|
#define HAM64_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
// buffer size required for the string representation of a maximum-length HAM64
|
||||||
|
// address, including terminating zero.
|
||||||
|
#define HAM64_FMT_MAX_LEN (4*4 + 3 + 1)
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
HAM64_ADDR_TYPE_EMPTY,
|
||||||
|
HAM64_ADDR_TYPE_TMP_SHORT,
|
||||||
|
HAM64_ADDR_TYPE_CALLSIGN,
|
||||||
|
HAM64_ADDR_TYPE_BROADCAST,
|
||||||
|
HAM64_ADDR_TYPE_IPV6_MULTICAST,
|
||||||
|
HAM64_ADDR_TYPE_IPV4_MULTICAST,
|
||||||
|
HAM64_ADDR_TYPE_RESERVED,
|
||||||
|
|
||||||
|
HAM64_NUM_ADDR_TYPES
|
||||||
|
} ham64_addr_type_t;
|
||||||
|
|
||||||
|
typedef struct ham64_s {
|
||||||
|
uint16_t addr[4];
|
||||||
|
uint8_t length;
|
||||||
|
} ham64_t;
|
||||||
|
|
||||||
|
/*!\brief Encode a call sign as HAM64 address.
|
||||||
|
* \param call The call sign as null-terminated string.
|
||||||
|
* \param ham64 Pointer to the memory for the output address.
|
||||||
|
* \returns The length in words of the encoded address. Zero if an error occurred.
|
||||||
|
*/
|
||||||
|
size_t ham64_encode(const char *call, ham64_t *ham64);
|
||||||
|
|
||||||
|
/*!\brief Decode a ham64 address to a readable call sign.
|
||||||
|
* \param ham64 Pointer to the address to be decoded.
|
||||||
|
* \param call Pointer to the output buffer (at least 13 characters in size).
|
||||||
|
* \returns The length of the output string or zero in case of errors.
|
||||||
|
*/
|
||||||
|
size_t ham64_decode_callsign(const ham64_t *ham64, char *call);
|
||||||
|
|
||||||
|
/*!\brief Determine the type of a HAM64 address.
|
||||||
|
* \param ham64 Pointer to the address to identify.
|
||||||
|
* \returns The type of the address.
|
||||||
|
*/
|
||||||
|
ham64_addr_type_t ham64_get_addr_type(const ham64_t *ham64);
|
||||||
|
|
||||||
|
/*!\brief Get a string representation of the given address type.
|
||||||
|
*/
|
||||||
|
const char *ham64_addr_type_to_string(ham64_addr_type_t addr_type);
|
||||||
|
|
||||||
|
/*!\brief Format a ham64 address into a string for printing.
|
||||||
|
* \param ham64 Pointer to the address to be formatted.
|
||||||
|
* \param out Pointer to the buffer where the string representation will
|
||||||
|
* be stored. Must be at least \ref HAM64_FMT_MAX_LEN bytes long.
|
||||||
|
*/
|
||||||
|
void ham64_format(const ham64_t *ham64, char *out);
|
||||||
|
|
||||||
|
#endif // HAM64_H
|
|
@ -132,3 +132,16 @@ target_link_libraries(
|
||||||
m
|
m
|
||||||
liquid
|
liquid
|
||||||
)
|
)
|
||||||
|
|
||||||
|
#------------------------------------
|
||||||
|
|
||||||
|
add_executable(
|
||||||
|
test_ham64
|
||||||
|
../src/layer2/ham64.c
|
||||||
|
../src/layer2/ham64.h
|
||||||
|
test_ham64.c
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(
|
||||||
|
test_ham64
|
||||||
|
)
|
||||||
|
|
50
impl/test/test_ham64.c
Normal file
50
impl/test/test_ham64.c
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include <layer2/ham64.h>
|
||||||
|
|
||||||
|
bool test_decode(const char *ref, const ham64_t *ham64)
|
||||||
|
{
|
||||||
|
char decoded[13];
|
||||||
|
decoded[12] = 0;
|
||||||
|
|
||||||
|
char ham64_format_buf[HAM64_FMT_MAX_LEN];
|
||||||
|
|
||||||
|
size_t decoded_len = ham64_decode_callsign(ham64, decoded);
|
||||||
|
|
||||||
|
ham64_format(ham64, ham64_format_buf);
|
||||||
|
const char *typestr = ham64_addr_type_to_string(ham64_get_addr_type(ham64));
|
||||||
|
printf("»%s« → [%u] %s (%s) → [%zd] »%s«\n", ref, ham64->length, ham64_format_buf, typestr, decoded_len, decoded);
|
||||||
|
|
||||||
|
return strcmp(ref, decoded) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool test_call(const char *call)
|
||||||
|
{
|
||||||
|
ham64_t ham64;
|
||||||
|
|
||||||
|
ham64_encode(call, &ham64);
|
||||||
|
return test_decode(call, &ham64);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
char *call1 = "VI2BMARC50-X";
|
||||||
|
char *call2 = "DL5TKL/T";
|
||||||
|
char *call3 = "D9K";
|
||||||
|
|
||||||
|
ham64_t empty = {{0x0000, 0x0, 0x0, 0x0}, 1};
|
||||||
|
ham64_t short_1 = {{0x0001, 0x0, 0x0, 0x0}, 1};
|
||||||
|
ham64_t short_last = {{0x063F, 0x0, 0x0, 0x0}, 1};
|
||||||
|
ham64_t broadcast = {{0xFFFF, 0x0, 0x0, 0x0}, 1};
|
||||||
|
|
||||||
|
test_call(call1);
|
||||||
|
test_call(call2);
|
||||||
|
test_call(call3);
|
||||||
|
|
||||||
|
test_decode("", &empty);
|
||||||
|
test_decode("", &short_1);
|
||||||
|
test_decode("", &short_last);
|
||||||
|
test_decode("", &broadcast);
|
||||||
|
}
|
Loading…
Reference in a new issue