/* Radiocom 2000 frame transcoding * * (C) 2017 by Andreas Eversberg * All Rights Reserved * * 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 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include "../libdebug/debug.h" #include "../libhagelbarger/hagelbarger.h" #include "frame.h" static const char *param_hex(uint64_t value) { static char result[32]; sprintf(result, "0x%" PRIx64, value); return result; } static const char *param_voie_rel(uint64_t value) { return (value) ? "Control Channel" : "Traffic Channel"; } static const char *param_voie_sm(uint64_t value) { return (value) ? "Traffic Channel" : "Control Channel"; } const char *param_agi(uint64_t value) { switch (value) { case 0: return "Prohibited control channel (no mobile allowed)"; case 1: return "New registration prohibited (registered mobiles allowed)"; case 2: return "Registration is reserved to test mobiles"; case 3: return "Registration for nominal mobiles (home network)"; case 4: return "Registration is reserved to special mobiles"; case 5: case 6: case 7: return "Registration permissible for all mobile station"; } return ""; } const char *param_aga(uint64_t value) { switch (value) { case 0: return "Outgoing calls prohibited"; case 1: return "Reserved (Outgoing calls prohibited)"; case 2: return "Outgoing call reserved for privileged mobiles"; case 3: return "Outgoing calls permissible"; } return ""; } const char *param_power(uint64_t value) { switch (value) { case 0: return "Low"; case 1: return "High"; } return ""; } const char *param_crins(uint64_t value) { switch (value) { case 0: return "Finished or just registering"; case 1: return "Localization impossible (queue full)"; case 2: return "Mobile station temporarily disabled"; case 3: return "Mobile station definitely disabled (WILL BRICK THE PHONE!)"; case 4: return "Blocked localization (BS out of order)"; case 5: case 6: return "Reserved"; case 7: return "Calling subscriber unknown"; } return ""; } const char *param_invitation(uint64_t value) { switch (value) { case 3: return "to Answer"; case 10: return "to Dial"; } return ""; } static const char *param_digit(uint64_t value) { static char result[32]; switch (value) { case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: case 9: sprintf(result, "'%c'", (int)value + '0'); return result; case 10: return "'*'"; case 11: return "'#'"; case 12: return "'A'"; case 13: return "'B'"; case 14: return "'C'"; case 15: return "'D'"; } return ""; } static struct r2000_element { char element; const char *name; const char *(*decoder_rel)(uint64_t value); /* REL sends to SM */ const char *(*decoder_sm)(uint64_t value); /* SM sends to REL */ } r2000_element[] = { { 'V', "Channel Type", param_voie_rel, param_voie_sm }, { 'C', "Channel", NULL, NULL }, { 'R', "Relais", NULL, NULL }, { 'M', "Message", NULL, NULL }, { 'D', "Deport", NULL, NULL }, { 'I', "AGI", param_agi, param_agi }, // { 'A', "AGA", param_aga, param_aga }, { 'P', "power", param_power, param_power }, { 'T', "taxe", NULL, NULL }, { 't', "SM Type", param_hex, param_hex }, { 'r', "SM Relais", NULL, NULL }, { 'f', "SM Flotte", NULL, NULL }, { 'm', "SM ID", NULL, NULL }, { 'd', "Called Flotte", NULL, NULL }, { 'c', "CRINS", param_crins, param_crins }, { 'a', "Assign Channel", NULL, NULL }, { 's', "Sequence Number", param_hex, param_hex }, { 'i', "Invitation", param_invitation,param_invitation }, { 'n', "NCONV", NULL, NULL }, { '0', "1st Digit", param_digit, param_digit }, { '1', "2nd Digit", param_digit, param_digit }, { '2', "3rd Digit", param_digit, param_digit }, { '3', "4th Digit", param_digit, param_digit }, { '4', "5th Digit", param_digit, param_digit }, { '5', "6th Digit", param_digit, param_digit }, { '6', "7th Digit", param_digit, param_digit }, { '7', "8th Digit", param_digit, param_digit }, { '8', "9th Digit", param_digit, param_digit }, { '9', "10th Digit", param_digit, param_digit }, { '?', "Unknown", param_hex, param_hex }, { 0, NULL, NULL, NULL } }; static void print_element(char element, uint64_t value, int dir, int debug) { const char *(*decoder)(uint64_t value); int i; for (i = 0; r2000_element[i].element; i++) { if (r2000_element[i].element == element) break; } decoder = (dir == REL_TO_SM) ? r2000_element[i].decoder_rel : r2000_element[i].decoder_sm; if (!r2000_element[i].element) PDEBUG(DFRAME, debug, "Element '%c' %" PRIu64 " [Unknown]\n", element, value); else if (!decoder) PDEBUG(DFRAME, debug, "Element '%c' %" PRIu64 " [%s]\n", element, value, r2000_element[i].name); else PDEBUG(DFRAME, debug, "Element '%c' %" PRIu64 "=%s [%s]\n", element, value, decoder(value), r2000_element[i].name); } static void store_element(frame_t *frame, char element, uint64_t value) { switch(element) { case 'V': frame->voie = value; break; case 'C': frame->channel = value; break; case 'R': frame->relais = value; break; case 'M': frame->message = value; break; case 'D': frame->deport = value; break; case 'I': frame->agi = value; break; case 'P': frame->sm_power = value; break; case 'T': frame->taxe = value; break; case 't': frame->sm_type = value; break; case 'r': frame->sm_relais = value; break; case 'f': frame->sm_flotte = value; break; case 'm': frame->sm_mor = value; break; case 'd': frame->sm_mop_demandee = value; break; case 'c': frame->crins = value; break; case 'a': frame->chan_assign = value; break; case 's': frame->sequence = value; break; case 'i': frame->invitation = value; break; case 'n': frame->nconv = value; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': frame->digit[element - '0'] = value; break; } } static uint64_t fetch_element(frame_t *frame, char element) { switch(element) { case 'V': return frame->voie; case 'C': return frame->channel; case 'R': return frame->relais; case 'M': return frame->message; case 'D': return frame->deport; case 'I': return frame->agi; case 'P': return frame->sm_power; case 'T': return frame->taxe; case 't': return frame->sm_type; case 'r': return frame->sm_relais; case 'f': return frame->sm_flotte; case 'm': return frame->sm_mor; case 'd': return frame->sm_mop_demandee; case 'c': return frame->crins; case 'a': return frame->chan_assign; case 's': return frame->sequence; case 'i': return frame->invitation; case 'n': return frame->nconv; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return frame->digit[element - '0']; } return 0; } static struct r2000_frame { int dir; uint8_t message; const char *def; const char *name; } r2000_frame_def[] = { /* V Channel-Relais---Msg--t--HomeRel--MobieID---------misc--------Supervisory----- */ /* messages REL->SM */ { REL_TO_SM, 0, "V-CCCCCCCCRRRRRRRRRMMMMMtttrrrrrrrrrmmmmmmmmmmmmmmmm-----ccc----DDDIII++---PT---", "INSCRIPTION ACK" }, /* inscription ack */ { REL_TO_SM, 1, "V-CCCCCCCCRRRRRRRRRMMMMM----------------------------------------DDDIII++---PT---", "IDLE" }, /* broadcast */ { REL_TO_SM, 2, "V-CCCCCCCCRRRRRRRRRMMMMMtttrrrrrrrrrmmmmmmmmmmmmmmmm------------DDDIII++---PT---", "PLEASE WAIT" }, /* waiting on CC */ { REL_TO_SM, 3, "V-CCCCCCCCRRRRRRRRRMMMMMtttrrrrrrrrrmmmmmmmmmmmmmmmmaaaaaaaa----DDDIII++---PT---", "ASSIGN INCOMING"}, /* assign incoming call */ { REL_TO_SM, 4, "V-CCCCCCCCRRRRRRRRRMMMMMtttrrrrrrrrrfffffffffmmmmmmmaaaaaaaa----DDDIII++---PT---", "ASSIGN INCOMING (GROUP)"}, /* assign group call */ { REL_TO_SM, 5, "V-CCCCCCCCRRRRRRRRRMMMMMtttrrrrrrrrrmmmmmmmmmmmmmmmmaaaaaaaa----DDDIII++---PT---", "ASSIGN OUTGOING"}, /* assign outgoing call */ { REL_TO_SM, 9, "V-CCCCCCCCRRRRRRRRRMMMMMtttrrrrrrrrrmmmmmmmmmmmmmmmm------------DDDIII++---PT---", "RELEASE ON CC" }, /* release call on CC */ { REL_TO_SM, 16, "V-CCCCCCCCRRRRRRRRRMMMMMtttrrrrrrrrrmmmmmmmmmmmmmmmm----------------------------", "IDENTITY REQ"}, /* request identity */ { REL_TO_SM, 17, "V-CCCCCCCCRRRRRRRRRMMMMMtttrrrrrrrrrmmmmmmmmmmmmmmmm-----nnniiii----------------", "INVITATION"}, /* invitation */ { REL_TO_SM, 24, "V-CCCCCCCCRRRRRRRRRMMMMMtttrrrrrrrrrmmmmmmmmmmmmmmmm----------------------------", "RELEASE ON TC"}, /* release call */ { REL_TO_SM, 26, "V-CCCCCCCCRRRRRRRRRMMMMMtttrrrrrrrrrmmmmmmmmmmmmmmmm----------------------------", "SUSPEND REQ"}, /* suspend after dialing */ /* messages SM->REL */ { SM_TO_REL, 0, "V-CCCCCCCCRRRRRRRRRMMMMMtttrrrrrrrrrmmmmmmmmmmmmmmmm--------ssss", "INSCRIPTION REQ" }, /* inscription */ { SM_TO_REL, 1, "V-CCCCCCCCRRRRRRRRRMMMMMtttrrrrrrrrrmmmmmmmmmmmmmmmm--------ssss", "CALL REQ (PRIVATE)" }, /* request call */ { SM_TO_REL, 2, "V-CCCCCCCCRRRRRRRRRMMMMMtttrrrrrrrrrfffffffffmmmmmmmddddddddssss", "CALL REQ (GROUP)" }, /* request call */ { SM_TO_REL, 3, "V-CCCCCCCCRRRRRRRRRMMMMMtttrrrrrrrrrmmmmmmmmmmmmmmmm--------ssss", "CALL REQ (PUBLIC)" }, /* request call */ { SM_TO_REL, 6, "V-CCCCCCCCRRRRRRRRRMMMMMtttrrrrrrrrrmmmmmmmmmmmmmmmm------------", "RELEASE ON CC" }, /* release on CC */ { SM_TO_REL, 16, "V-CCCCCCCCRRRRRRRRRMMMMMtttrrrrrrrrrmmmmmmmmmmmmmmmm--------ssss", "IDENTITY ACK" }, /* identity response */ { SM_TO_REL, 17, "V-CCCCCCCCRRRRRRRRRMMMMMtttrrrrrrrrrmmmmmmmmmmmmmmmm------------", "ANSWER" }, /* answer */ { SM_TO_REL, 19, "V-CCCCCCCCRRRRRRRRRMMMMM1111000033332222555544447777666699998888", "DIAL 1..10" }, /* first 10 digits */ { SM_TO_REL, 20, "V-CCCCCCCCRRRRRRRRRMMMMM1111000033332222555544447777666699998888", "DIAL 11..20" }, /* second 10 digits */ { SM_TO_REL, 24, "V-CCCCCCCCRRRRRRRRRMMMMMtttrrrrrrrrrmmmmmmmmmmmmmmmm------------", "RELEASE ON TC" }, /* release call on TC */ { SM_TO_REL, 26, "V-CCCCCCCCRRRRRRRRRMMMMMtttrrrrrrrrrmmmmmmmmmmmmmmmm------------", "SUSPEND ACK" }, /* release after dialing */ { 0, 0, NULL, NULL } }; static const char *get_frame_def(uint8_t message, int dir) { int i; for (i = 0; r2000_frame_def[i].def; i++) { if (r2000_frame_def[i].message == message && r2000_frame_def[i].dir == dir) return r2000_frame_def[i].def; } return NULL; } const char *r2000_dir_name(int dir) { return (dir == REL_TO_SM) ? "REL->SM" : "SM->REL"; } const char *r2000_frame_name(int message, int dir) { static char result[32]; int i; for (i = 0; r2000_frame_def[i].def; i++) { if (r2000_frame_def[i].message == message && r2000_frame_def[i].dir == dir) { sprintf(result, "%s (%d)", r2000_frame_def[i].name, message); return result; } } sprintf(result, "UNKNOWN (%d)", message); return result; } static void display_bits(const char *def, const uint8_t *message, int num, int debug) { char dispbits[num + 1]; int i; if (debuglevel > debug) return; /* display bits */ if (def) PDEBUG(DFRAME, debug, "%s\n", def); for (i = 0; i < num; i++) { dispbits[i] = ((message[i / 8] >> (7 - (i & 7))) & 1) + '0'; } dispbits[i] = '\0'; PDEBUG(DFRAME, debug, "%s\n", dispbits); } static int dissassemble_frame(frame_t *frame, const uint8_t *message, int num) { int i; const char *def; uint64_t value; int dir = (num == 80) ? REL_TO_SM : SM_TO_REL; memset(frame, 0, sizeof(*frame)); frame->message = message[2] & 0x1f; def = get_frame_def(frame->message, dir); if (!def) { PDEBUG(DFRAME, DEBUG_NOTICE, "Received unknown message type %d (maybe radio noise)\n", frame->message); display_bits(NULL, message, num, DEBUG_NOTICE); return -EINVAL; } PDEBUG(DFRAME, DEBUG_DEBUG, "Decoding frame %s %s\n", r2000_dir_name(dir), r2000_frame_name(frame->message, dir)); /* disassemble elements elements */ value = 0; for (i = 0; i < num; i++) { value = (value << 1) | ((message[i / 8] >> (7 - (i & 7))) & 1); if (def[i + 1] != def[i]) { if (def[i] != '-') { print_element(def[i], value, dir, DEBUG_DEBUG); store_element(frame, def[i], value); } value = 0; } } display_bits(def, message, num, DEBUG_DEBUG); return 0; } static int assemble_frame(frame_t *frame, uint8_t *message, int num, int debug) { int i; const char *def; uint64_t value = 0; // make GCC happy char element; int dir = (num == 80) ? REL_TO_SM : SM_TO_REL; def = get_frame_def(frame->message, dir); if (!def) { PDEBUG(DFRAME, DEBUG_ERROR, "Cannot assemble unknown message type %d, please define/fix!\n", frame->message); abort(); } memset(message, 0, (num + 7) / 8); if (debug) PDEBUG(DFRAME, DEBUG_DEBUG, "Ccoding frame %s %s\n", r2000_dir_name(dir), r2000_frame_name(frame->message, dir)); /* assemble elements elements */ element = 0; for (i = num - 1; i >= 0; i--) { if (element != def[i]) { element = def[i]; switch (def[i]) { case '-': value = 0; break; case '+': value = 0xffffffffffffffff; break; default: value = fetch_element(frame, element); } } message[i / 8] |= (value & 1) << (7 - (i & 7)); value >>= 1; } if (debug) { for (i = 0; i < num; i++) { if (def[i + 1] != def[i] && def[i] != '-' && def[i] != '+') { value = fetch_element(frame, def[i]); print_element(def[i], value, dir, DEBUG_DEBUG); } } display_bits(def, message, num, DEBUG_DEBUG); } return 0; } /* encode frame to bits */ const char *encode_frame(frame_t *frame, int debug) { uint8_t message[11], code[23]; static char bits[32 + 176 + 1]; int i; assemble_frame(frame, message, 80, debug); /* hagelbarger code */ hagelbarger_encode(message, code, 88); memcpy(bits, "10101010101010101010111100010010", 32); for (i = 0; i < 176; i++) bits[i + 32] = ((code[i / 8] >> (7 - (i & 7))) & 1) + '0'; bits[208] = '\0'; return bits; } //#define GEGENPROBE /* decode bits to frame */ int decode_frame(frame_t *frame, const char *bits) { uint8_t message[11], code[23]; int i, num = strlen(bits); #ifdef GEGENPROBE printf("bits as received=%s\n", bits); #endif /* hagelbarger code */ memset(code, 0x00, sizeof(code)); for (i = 0; i < num; i++) code[i / 8] |= (bits[i] & 1) << (7 - (i & 7)); hagelbarger_decode(code, message, num / 2 - 6); #if 0 for (i = 0; i < num / 2; i++) { printf("%d", (message[i / 8] >> (7 - (i & 7))) & 1); if ((i & 7) == 7) printf(" = 0x%02x\n", message[i / 8]); } #endif #ifdef GEGENPROBE hagelbarger_encode(message, code, num / 2); printf("bits after re-encoding="); for (i = 0; i < num; i++) printf("%d", (code[i / 8] >> (7 - (i & 7))) & 1); printf("\n"); #endif return dissassemble_frame(frame, message, num / 2 - 8); }