mirror of
https://github.com/peterantypas/maiana.git
synced 2025-06-15 06:00:21 -07:00
WIP
This commit is contained in:
parent
3cd8ed4e22
commit
f9280b35ea
@ -34,7 +34,7 @@ public:
|
|||||||
// Called directly by each receiver to report every RSSI reading at every SOTDMA slot
|
// Called directly by each receiver to report every RSSI reading at every SOTDMA slot
|
||||||
void report(char channel, uint8_t rssi);
|
void report(char channel, uint8_t rssi);
|
||||||
|
|
||||||
void reset();
|
void recalculate();
|
||||||
|
|
||||||
// Returns the current noise floor of the channel, 0xff if unknown
|
// Returns the current noise floor of the channel, 0xff if unknown
|
||||||
uint8_t getNoiseFloor(char channel);
|
uint8_t getNoiseFloor(char channel);
|
||||||
|
@ -73,8 +73,18 @@
|
|||||||
// Maximum allowed backlog in TX queue
|
// Maximum allowed backlog in TX queue
|
||||||
#define MAX_TX_PACKETS_IN_QUEUE 4
|
#define MAX_TX_PACKETS_IN_QUEUE 4
|
||||||
|
|
||||||
|
|
||||||
|
// Set to true to force RSSI sampling at every SOTDMA timer slot on both channels
|
||||||
|
#define FULL_RSSI_SAMPLING 1
|
||||||
|
|
||||||
// Headroom above noise floor (in dB) that constitutes a clear channel for transmission
|
// Headroom above noise floor (in dB) that constitutes a clear channel for transmission
|
||||||
#define TX_CCA_HEADROOM 3
|
#if FULL_RSSI_SAMPLING
|
||||||
|
#define TX_CCA_HEADROOM 2
|
||||||
|
#else
|
||||||
|
// When sampling RSSI sparsely, there is a tendency to overestimate the noise floor, so there is no need for headroom
|
||||||
|
#define TX_CCA_HEADROOM 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
// Transmission intervals in seconds
|
// Transmission intervals in seconds
|
||||||
#define MIN_TX_INTERVAL 5
|
#define MIN_TX_INTERVAL 5
|
||||||
|
@ -107,8 +107,8 @@ void AISMessage::addString(uint8_t *bitVector, uint16_t &size, const string &val
|
|||||||
ASSERT(value.length() <= maxChars);
|
ASSERT(value.length() <= maxChars);
|
||||||
ASSERT(maxChars < 30); // There should be no application for such long strings here
|
ASSERT(maxChars < 30); // There should be no application for such long strings here
|
||||||
char s[30];
|
char s[30];
|
||||||
memset(s, 0, sizeof s);
|
//memset(s, 0, sizeof s);
|
||||||
strncpy(s, value.c_str(), value.length());
|
strlcpy(s, value.c_str(), sizeof s);
|
||||||
|
|
||||||
uint8_t buffer[32];
|
uint8_t buffer[32];
|
||||||
for ( uint8_t c = 0; c < maxChars; ++c ) {
|
for ( uint8_t c = 0; c < maxChars; ++c ) {
|
||||||
|
@ -107,8 +107,8 @@ void CommandProcessor::processCommand(const char *buff)
|
|||||||
|
|
||||||
memset(&station, 0, sizeof station);
|
memset(&station, 0, sizeof station);
|
||||||
station.mmsi = Utils::toInt(tokens[0]);
|
station.mmsi = Utils::toInt(tokens[0]);
|
||||||
strncpy(station.name, tokens[1].c_str(), sizeof station.name);
|
strlcpy(station.name, tokens[1].c_str(), sizeof station.name);
|
||||||
strncpy(station.callsign, tokens[2].c_str(), sizeof station.callsign);
|
strlcpy(station.callsign, tokens[2].c_str(), sizeof station.callsign);
|
||||||
int type = (VesselType)Utils::toInt(tokens[3]);
|
int type = (VesselType)Utils::toInt(tokens[3]);
|
||||||
if ( type == 30 || type == 34 || type == 36 || type == 37 )
|
if ( type == 30 || type == 34 || type == 36 || type == 37 )
|
||||||
station.type = (VesselType)type;
|
station.type = (VesselType)type;
|
||||||
|
@ -112,7 +112,7 @@ void termInputCB(char c)
|
|||||||
{
|
{
|
||||||
__rxbuff[__rxpos++] = 0;
|
__rxbuff[__rxpos++] = 0;
|
||||||
Event *e = EventPool::instance().newEvent(COMMAND_EVENT);
|
Event *e = EventPool::instance().newEvent(COMMAND_EVENT);
|
||||||
strncpy(e->command.buffer, __rxbuff, sizeof e->command.buffer);
|
strlcpy(e->command.buffer, __rxbuff, sizeof e->command.buffer);
|
||||||
EventQueue::instance().push(e);
|
EventQueue::instance().push(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -116,7 +116,7 @@ void GPS::onRX(char c)
|
|||||||
Event *e = EventPool::instance().newEvent(GPS_NMEA_SENTENCE);
|
Event *e = EventPool::instance().newEvent(GPS_NMEA_SENTENCE);
|
||||||
if ( e )
|
if ( e )
|
||||||
{
|
{
|
||||||
strncpy(e->nmeaBuffer.sentence, mBuff, sizeof e->nmeaBuffer.sentence);
|
strlcpy(e->nmeaBuffer.sentence, mBuff, sizeof e->nmeaBuffer.sentence);
|
||||||
EventQueue::instance ().push(e);
|
EventQueue::instance ().push(e);
|
||||||
}
|
}
|
||||||
mBuffPos = 0;
|
mBuffPos = 0;
|
||||||
|
@ -46,18 +46,9 @@ NoiseFloorDetector::NoiseFloorDetector()
|
|||||||
|
|
||||||
void NoiseFloorDetector::report(char channel, uint8_t rssi)
|
void NoiseFloorDetector::report(char channel, uint8_t rssi)
|
||||||
{
|
{
|
||||||
if ( rssi < 0x20 ) // Not realistic, likely a bug
|
if ( rssi < 0x32 ) // Not realistic, likely a bug
|
||||||
return;
|
return;
|
||||||
|
|
||||||
#if 0
|
|
||||||
processSample(channel == 'A' ? mChannelASamples : mChannelBSamples, rssi);
|
|
||||||
|
|
||||||
if ( channel == 'A' )
|
|
||||||
mChannelACurrent = medianValue(mChannelASamples);
|
|
||||||
else
|
|
||||||
mChannelBCurrent = medianValue(mChannelBSamples);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if ( channel == 'A' )
|
if ( channel == 'A' )
|
||||||
{
|
{
|
||||||
mAFloor = min(mAFloor, rssi);
|
mAFloor = min(mAFloor, rssi);
|
||||||
@ -90,61 +81,16 @@ void NoiseFloorDetector::processEvent(const Event &e)
|
|||||||
if ( mTicks == 30 )
|
if ( mTicks == 30 )
|
||||||
{
|
{
|
||||||
//DBG("Event pool utilization = %d, max = %d\r\n", EventPool::instance().utilization(), EventPool::instance().maxUtilization());
|
//DBG("Event pool utilization = %d, max = %d\r\n", EventPool::instance().utilization(), EventPool::instance().maxUtilization());
|
||||||
mChannelACurrent = mAFloor;
|
recalculate();
|
||||||
mChannelBCurrent = mBFloor;
|
|
||||||
dump();
|
dump();
|
||||||
reset();
|
|
||||||
mTicks = 0;
|
mTicks = 0;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
#if 0
|
|
||||||
case RSSI_SAMPLE_EVENT:
|
|
||||||
{
|
|
||||||
report(e.rssiSample.channel, e.rssiSample.rssi);
|
|
||||||
uint8_t rssi = getNoiseFloor(e.rssiSample.channel);
|
|
||||||
RadioManager::instance().noiseFloorUpdated(e.rssiSample.channel, rssi);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
#endif
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
|
||||||
void NoiseFloorDetector::processSample(ChannelReadings &window, uint8_t rssi)
|
|
||||||
{
|
|
||||||
while ( window.size() >= WINDOW_SIZE )
|
|
||||||
window.pop_back();
|
|
||||||
|
|
||||||
if ( window.empty() )
|
|
||||||
{
|
|
||||||
window.push_back(rssi);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insert the reading at the start if it qualifies
|
|
||||||
for ( ChannelReadings::iterator i = window.begin(); i != window.end(); ++i )
|
|
||||||
{
|
|
||||||
if ( rssi <= *i )
|
|
||||||
{
|
|
||||||
window.insert(i, rssi);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t NoiseFloorDetector::medianValue(ChannelReadings &window)
|
|
||||||
{
|
|
||||||
if ( window.size() < WINDOW_SIZE )
|
|
||||||
return 0xff;
|
|
||||||
|
|
||||||
return window[window.size()/2];
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void NoiseFloorDetector::dump()
|
void NoiseFloorDetector::dump()
|
||||||
{
|
{
|
||||||
Event *e = EventPool::instance().newEvent(PROPR_NMEA_SENTENCE);
|
Event *e = EventPool::instance().newEvent(PROPR_NMEA_SENTENCE);
|
||||||
@ -164,10 +110,19 @@ void NoiseFloorDetector::dump()
|
|||||||
EventQueue::instance().push(e);
|
EventQueue::instance().push(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NoiseFloorDetector::reset()
|
void NoiseFloorDetector::recalculate()
|
||||||
{
|
{
|
||||||
//mChannelASamples.clear();
|
if ( mChannelACurrent == 0xff || mChannelBCurrent == 0xff )
|
||||||
//mChannelBSamples.clear();
|
{
|
||||||
|
mChannelACurrent = mAFloor;
|
||||||
|
mChannelBCurrent = mBFloor;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mChannelACurrent = (mChannelACurrent + mAFloor) / 2;
|
||||||
|
mChannelBCurrent = (mChannelBCurrent + mBFloor) / 2;
|
||||||
|
}
|
||||||
|
|
||||||
mAFloor = 0xff;
|
mAFloor = 0xff;
|
||||||
mBFloor = 0xff;
|
mBFloor = 0xff;
|
||||||
}
|
}
|
||||||
|
@ -150,16 +150,24 @@ void Receiver::onBitClock()
|
|||||||
}
|
}
|
||||||
#if ENABLE_TX
|
#if ENABLE_TX
|
||||||
/**
|
/**
|
||||||
* This trick ensures that we only sample RSSI every 17 time slots and never in the
|
* This trick ensures that we only sample RSSI every N time slots and never in the
|
||||||
* same time slot for both ICs, so we don't conduct long SPI operations on consecutive
|
* same time slot for both ICs, so we don't conduct long SPI operations on consecutive
|
||||||
* interrupt handlers that might exceed the bit clock period. There is no reason for RSSI
|
* interrupt handlers that might exceed the bit clock period. There is no reason for RSSI
|
||||||
* collection to have a high duty cycle anyway, it just serves to establish the noise floor.
|
* collection to have a high duty cycle anyway, it just serves to establish the noise floor.
|
||||||
*/
|
*/
|
||||||
else if ( mTimeSlot != 0xffffffff && mSlotBitNumber != 0xffff &&
|
#if FULL_RSSI_SAMPLING
|
||||||
mTimeSlot % 17 == mChipID && mSlotBitNumber == CCA_SLOT_BIT - 1 )
|
else if ( mTimeSlot != 0xffffffff && mSlotBitNumber != 0xffff && mSlotBitNumber == CCA_SLOT_BIT )
|
||||||
{
|
{
|
||||||
reportRSSI();
|
mRXPacket->setRSSI(reportRSSI());
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
else if ( mTimeSlot != 0xffffffff && mSlotBitNumber != 0xffff &&
|
||||||
|
mTimeSlot % 17 == mChipID &&
|
||||||
|
mSlotBitNumber == CCA_SLOT_BIT - 1 )
|
||||||
|
{
|
||||||
|
mRXPacket->setRSSI(reportRSSI());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
//bsp_signal_low();
|
//bsp_signal_low();
|
||||||
|
@ -73,7 +73,7 @@ time_t TXPacket::timestamp()
|
|||||||
|
|
||||||
void TXPacket::setMessageType(const char *t)
|
void TXPacket::setMessageType(const char *t)
|
||||||
{
|
{
|
||||||
strncpy(mMessageType, t, sizeof mMessageType);
|
strlcpy(mMessageType, t, sizeof mMessageType);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TXPacket::isTestPacket()
|
bool TXPacket::isTestPacket()
|
||||||
|
@ -217,7 +217,12 @@ void Transceiver::onBitClock()
|
|||||||
}
|
}
|
||||||
else if ( mUTC && mSlotBitNumber == CCA_SLOT_BIT && mTXPacket->channel() == mChannel )
|
else if ( mUTC && mSlotBitNumber == CCA_SLOT_BIT && mTXPacket->channel() == mChannel )
|
||||||
{
|
{
|
||||||
|
#if FULL_RSSI_SAMPLING
|
||||||
|
// It has already been sampled during Receiver::onBitClock();
|
||||||
|
int rssi = mRXPacket->rssi();
|
||||||
|
#else
|
||||||
int rssi = readRSSI();
|
int rssi = readRSSI();
|
||||||
|
#endif
|
||||||
int nf = NoiseFloorDetector::instance().getNoiseFloor(AIS_CHANNELS[mChannel].designation);
|
int nf = NoiseFloorDetector::instance().getNoiseFloor(AIS_CHANNELS[mChannel].designation);
|
||||||
if ( rssi <= nf + TX_CCA_HEADROOM )
|
if ( rssi <= nf + TX_CCA_HEADROOM )
|
||||||
{
|
{
|
||||||
|
@ -58,7 +58,7 @@ static const GPIO __gpios[] = {
|
|||||||
{TX_DISABLE_PORT, {TX_DISABLE_PIN, GPIO_MODE_INPUT, GPIO_PULLUP, GPIO_SPEED_LOW, 0}, GPIO_PIN_SET},
|
{TX_DISABLE_PORT, {TX_DISABLE_PIN, GPIO_MODE_INPUT, GPIO_PULLUP, GPIO_SPEED_LOW, 0}, GPIO_PIN_SET},
|
||||||
{CS2_PORT, {CS2_PIN, GPIO_MODE_OUTPUT_PP, GPIO_NOPULL, GPIO_SPEED_HIGH, 0}, GPIO_PIN_SET},
|
{CS2_PORT, {CS2_PIN, GPIO_MODE_OUTPUT_PP, GPIO_NOPULL, GPIO_SPEED_HIGH, 0}, GPIO_PIN_SET},
|
||||||
{RX_EVT_PORT, {RX_EVT_PIN, GPIO_MODE_OUTPUT_PP, GPIO_NOPULL, GPIO_SPEED_LOW, 0}, GPIO_PIN_RESET},
|
{RX_EVT_PORT, {RX_EVT_PIN, GPIO_MODE_OUTPUT_PP, GPIO_NOPULL, GPIO_SPEED_LOW, 0}, GPIO_PIN_RESET},
|
||||||
{GNSS_1PPS_PORT, {GNSS_1PPS_PIN, GPIO_MODE_IT_FALLING, GPIO_PULLUP, GPIO_SPEED_LOW, 0}, GPIO_PIN_RESET},
|
{GNSS_1PPS_PORT, {GNSS_1PPS_PIN, GPIO_MODE_IT_FALLING, GPIO_NOPULL, GPIO_SPEED_LOW, 0}, GPIO_PIN_RESET},
|
||||||
{GNSS_NMEA_RX_PORT, {GNSS_NMEA_RX_PIN, GPIO_MODE_AF_PP, GPIO_PULLUP, GPIO_SPEED_LOW, GPIO_AF7_USART2}, GPIO_PIN_RESET},
|
{GNSS_NMEA_RX_PORT, {GNSS_NMEA_RX_PIN, GPIO_MODE_AF_PP, GPIO_PULLUP, GPIO_SPEED_LOW, GPIO_AF7_USART2}, GPIO_PIN_RESET},
|
||||||
{CS1_PORT, {CS1_PIN, GPIO_MODE_OUTPUT_PP, GPIO_NOPULL, GPIO_SPEED_HIGH, 0}, GPIO_PIN_SET},
|
{CS1_PORT, {CS1_PIN, GPIO_MODE_OUTPUT_PP, GPIO_NOPULL, GPIO_SPEED_HIGH, 0}, GPIO_PIN_SET},
|
||||||
{SCK_PORT, {SCK_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, GPIO_SPEED_HIGH, GPIO_AF5_SPI1}, GPIO_PIN_SET},
|
{SCK_PORT, {SCK_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, GPIO_SPEED_HIGH, GPIO_AF5_SPI1}, GPIO_PIN_SET},
|
||||||
@ -384,7 +384,6 @@ void bsp_gnss_on()
|
|||||||
void bsp_gnss_off()
|
void bsp_gnss_off()
|
||||||
{
|
{
|
||||||
HAL_GPIO_WritePin(GNSS_EN_PORT, GNSS_EN_PIN, GPIO_PIN_RESET);
|
HAL_GPIO_WritePin(GNSS_EN_PORT, GNSS_EN_PIN, GPIO_PIN_RESET);
|
||||||
HAL_Delay(10);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -541,6 +540,8 @@ void bsp_enter_dfu()
|
|||||||
// Cut off the GPS signals immediately to prevent its UART from transmitting and hijacking the bootloader upon reset
|
// Cut off the GPS signals immediately to prevent its UART from transmitting and hijacking the bootloader upon reset
|
||||||
bsp_gnss_off();
|
bsp_gnss_off();
|
||||||
|
|
||||||
|
HAL_Delay(1000);
|
||||||
|
|
||||||
// This flag simply tells main() to jump to the ROM bootloader immediately upon reset, before initializing anything
|
// This flag simply tells main() to jump to the ROM bootloader immediately upon reset, before initializing anything
|
||||||
*(uint32_t*)DFU_FLAG_ADDRESS = DFU_FLAG_MAGIC;
|
*(uint32_t*)DFU_FLAG_ADDRESS = DFU_FLAG_MAGIC;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user