SystemK/Protocols/Laser_X.c
2025-01-25 13:45:14 -06:00

212 lines
6.9 KiB
C
Executable file

/*
* This program source code file is part of SystemK, a library in the KTag project.
*
* 🛡️ <https://ktag.clubk.club> 🃞
*
* Copyright © 2016-2025 Joseph P. Kearney and the KTag developers.
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero 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 Affero General Public License for more
* details.
*
* There should be a copy of the GNU Affero General Public License in the LICENSE
* file in the root of this repository. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdbool.h>
#include <stdio.h>
#include "SystemK.h"
#define LASER_X_PURPLE_TEAM 0b00
#define LASER_X_RED_TEAM 0b01
#define LASER_X_BLUE_TEAM 0b10
#define RED_TEAM_PACKET_DATA 0x4A
#define BLUE_TEAM_PACKET_DATA 0x8A
#define PURPLE_TEAM_PACKET_DATA 0xCA
#define LASER_X_HEADER_MARK_DURATION_IN_us 6500
#define LASER_X_SPACE_DURATION_IN_us 500
#define LASER_X_MARK_ZERO_DURATION_IN_us 500
#define LASER_X_MARK_ONE_DURATION_IN_us 1500
#define LASER_X_TOLERANCE_IN_us 450
#define MIN_MARK_ZERO_IN_us (LASER_X_MARK_ZERO_DURATION_IN_us - LASER_X_TOLERANCE_IN_us)
#define MAX_MARK_ZERO_IN_us (LASER_X_MARK_ZERO_DURATION_IN_us + LASER_X_TOLERANCE_IN_us)
#define MIN_MARK_ONE_IN_us (LASER_X_MARK_ONE_DURATION_IN_us - LASER_X_TOLERANCE_IN_us)
#define MAX_MARK_ONE_IN_us (LASER_X_MARK_ONE_DURATION_IN_us + LASER_X_TOLERANCE_IN_us)
static const TimedBit_T LASER_X_HEADER_MARK = {.symbol = MARK, .duration = LASER_X_HEADER_MARK_DURATION_IN_us};
static const TimedBit_T LASER_X_SPACE = {.symbol = SPACE, .duration = LASER_X_SPACE_DURATION_IN_us};
static const TimedBit_T LASER_X_ONE = {.symbol = MARK, .duration = LASER_X_MARK_ONE_DURATION_IN_us};
static const TimedBit_T LASER_X_ZERO = {.symbol = MARK, .duration = LASER_X_MARK_ZERO_DURATION_IN_us};
static const TimedBit_T LASER_X_END = {.symbol = SPACE, .duration = LAST_PULSE};
static TimedPulseTrain_T Tag_Send_Buffer;
static DecodedPacket_T Laser_X_Tag_Received_Buffers[3] =
{
{.Tag.type = DECODED_PACKET_TYPE_BUFFER_FREE},
{.Tag.type = DECODED_PACKET_TYPE_BUFFER_FREE},
{.Tag.type = DECODED_PACKET_TYPE_BUFFER_FREE}};
static inline DecodedPacket_T *Get_Tag_Packet_Buffer(void)
{
if (Laser_X_Tag_Received_Buffers[0].Tag.type == DECODED_PACKET_TYPE_BUFFER_FREE)
{
return &Laser_X_Tag_Received_Buffers[0];
}
else if (Laser_X_Tag_Received_Buffers[1].Tag.type == DECODED_PACKET_TYPE_BUFFER_FREE)
{
return &Laser_X_Tag_Received_Buffers[1];
}
else
{
// Just use it.
return &Laser_X_Tag_Received_Buffers[2];
}
}
static inline void PackPulseTrain(TimedPulseTrain_T *pulsetrain, uint8_t data)
{
pulsetrain->bitstream[0] = LASER_X_HEADER_MARK;
pulsetrain->bitstream[1] = LASER_X_SPACE;
for (uint8_t n = 0; n < 8; n++)
{
if ((data & 0x01) == 0x01)
{
pulsetrain->bitstream[2 + (n * 2)] = LASER_X_ONE;
}
else
{
pulsetrain->bitstream[2 + (n * 2)] = LASER_X_ZERO;
}
pulsetrain->bitstream[2 + (n * 2) + 1] = LASER_X_SPACE;
data = data >> 1;
}
pulsetrain->bitstream[17] = LASER_X_END;
pulsetrain->count = 17;
}
TimedPulseTrain_T *LASER_X_EncodePacket(TagPacket_T *packet)
{
uint16_t packed_data = 0;
if (packet->team_ID == LASER_X_RED_TEAM)
{
packed_data = RED_TEAM_PACKET_DATA;
}
else if (packet->team_ID == LASER_X_BLUE_TEAM)
{
packed_data = BLUE_TEAM_PACKET_DATA;
}
else // LASER_X_PURPLE_TEAM
{
packed_data = PURPLE_TEAM_PACKET_DATA;
}
PackPulseTrain(&Tag_Send_Buffer, packed_data);
return &Tag_Send_Buffer;
}
DecodedPacket_T *LASER_X_MaybeDecodePacket(TimedPulseTrain_T *packet)
{
// Some implementations (ESP32) do not report odd numbers of pulses.
if ((packet->count != 17) && (packet->count != 18))
{
// Fail fast!
return NULL;
}
else
{
// Convert pulse durations to bits.
// packet->bitstream[0] is the header mark--ignore it.
// packet->bitstream[1] is the header space--ignore it also.
uint8_t data = 0;
for (uint8_t n = 0; n < 8; n++)
{
// Even pulses are marks; odd pulses are spaces.
if ((packet->bitstream[2 + (n * 2)].duration > MIN_MARK_ONE_IN_us) && (packet->bitstream[2 + (n * 2)].duration < MAX_MARK_ONE_IN_us))
{
// One
data |= ((uint8_t)1 << n);
}
else if ((packet->bitstream[2 + (n * 2)].duration > MIN_MARK_ZERO_IN_us) && (packet->bitstream[2 + (n * 2)].duration < MAX_MARK_ZERO_IN_us))
{
// Zero--nothing to do.
}
else
{
// This mark is neither a zero or a one; abort.
return NULL;
}
}
if (data == RED_TEAM_PACKET_DATA)
{
DecodedPacket_T *Tag_Rx_Buffer = Get_Tag_Packet_Buffer();
Tag_Rx_Buffer->Tag.type = DECODED_PACKET_TYPE_TAG_RECEIVED;
Tag_Rx_Buffer->Tag.protocol = LASER_X_PROTOCOL;
Tag_Rx_Buffer->Tag.player_ID = 0;
Tag_Rx_Buffer->Tag.team_ID = LASER_X_RED_TEAM;
Tag_Rx_Buffer->Tag.damage = 10;
Tag_Rx_Buffer->Tag.color = COLOR_RED;
return Tag_Rx_Buffer;
}
else if (data == BLUE_TEAM_PACKET_DATA)
{
DecodedPacket_T *Tag_Rx_Buffer = Get_Tag_Packet_Buffer();
Tag_Rx_Buffer->Tag.type = DECODED_PACKET_TYPE_TAG_RECEIVED;
Tag_Rx_Buffer->Tag.protocol = LASER_X_PROTOCOL;
Tag_Rx_Buffer->Tag.player_ID = 0;
Tag_Rx_Buffer->Tag.team_ID = LASER_X_BLUE_TEAM;
Tag_Rx_Buffer->Tag.damage = 10;
Tag_Rx_Buffer->Tag.color = COLOR_BLUE;
return Tag_Rx_Buffer;
}
else if (data == PURPLE_TEAM_PACKET_DATA)
{
DecodedPacket_T *Tag_Rx_Buffer = Get_Tag_Packet_Buffer();
Tag_Rx_Buffer->Tag.type = DECODED_PACKET_TYPE_TAG_RECEIVED;
Tag_Rx_Buffer->Tag.protocol = LASER_X_PROTOCOL;
Tag_Rx_Buffer->Tag.player_ID = 0;
Tag_Rx_Buffer->Tag.team_ID = LASER_X_PURPLE_TEAM;
Tag_Rx_Buffer->Tag.damage = 10;
Tag_Rx_Buffer->Tag.color = COLOR_PURPLE;
return Tag_Rx_Buffer;
}
return NULL;
}
}
color_t LASER_X_GetTeamColor(uint8_t team_ID)
{
color_t result = COLOR_PURPLE;
if (team_ID == LASER_X_RED_TEAM)
{
result = COLOR_RED;
}
else if (team_ID == LASER_X_BLUE_TEAM)
{
result = COLOR_BLUE;
}
else
{
// Nothing to do.
}
return result;
}