📄 gps source code.txt
字号:
// ****************************************************************************
// Real-time interrupt.
//
// A counter that ticks every 100mS.
uint8_t timeTicks;
// Counts the number of 1mS interrupts for a 100mS time period.
uint8_t timeInterruptCount;
// Counts the number of 100mS time periods in 1 second.
uint8_t time100ms;
// System time in seconds.
uint8_t timeSeconds;
// System time in minutes.
uint8_t timeMinutes;
// System time in hours.
uint8_t timeHours;
// Desired LED duty cycle 0 to 9.
uint8_t timeDutyCycle;
// Time compare register 1 value.
uint16_t timeCompare;
// The CCP1 timer delta between each interrupt. 1mS for PSK31 and 833uS for 1200 baud APRS
uint16_t timeRate;
// Flag set true once per second.
boolean timeUpdateFlag;
// The current number of interrupts in a 100mS time period. 100 for PSK31 and 120 for 1200 baud APRS.
uint8_t timeInterruptCountRollOver;
// The current time base mode.
TIME_MODE timeMode;
// The change in the CCP_1 register for each interrupt period. 1mS for PSK31, 833uS for 1200 baud APRS, 3.3mS for 300 baud HF-APRS
#define TIME_RATE_PSK31 1200
#define TIME_RATE_APRS 1000
#define TIME_RATE_HF_APRS 4000
// The number of interrupts in a 100mS time period.
#define TIME_ROLLOVER_PSK31 100
#define TIME_ROLLOVER_APRS 120
#define TIME_ROLLOVER_HF_APRS 30
/**
* Running 8-bit counter that ticks every 100mS.
*
* @return 100mS time tick
*/
uint8_t timeGetTicks()
{
return timeTicks;
}
/**
* Initialize the real-time clock.
*/
void timeInit()
{
timeTicks = 0;
timeInterruptCount = 0;
time100mS = 0;
timeSeconds = 0;
timeMinutes = 0;
timeHours = 0;
timeDutyCycle = TIME_DUTYCYCLE_70;
timeCompare = TIME_RATE_APRS;
timeUpdateFlag = false;
timeRate = TIME_RATE_APRS;
timeInterruptCountRollOver = TIME_ROLLOVER_APRS;
timeMode = TIME_MODE_APRS;
// Configure CCP1 to interrupt at 1mS for PSK31 or 833uS for 1200 baud APRS
CCP_1 = timeRate;
set_timer1(timeCompare);
setup_ccp1( CCP_COMPARE_INT );
setup_timer_1( T1_INTERNAL | T1_DIV_BY_4 );
}
/**
* Set the blink duty cycle of the heartbeat LED. The LED blinks at a 1Hz rate.
*
* @param dutyCycle TIME_DUTYCYCLE_xx constant
*/
void timeSetDutyCycle (uint8_t dutyCycle)
{
timeDutyCycle = dutyCycle;
}
/**
* Set the time base for PSK31 or APRS.
*
* @param mode <i>TIME_MODE_APRS</i> or <i>TIME_MODE_PSK31</i> constant
*/
void timeSetMode (TIME_MODE mode)
{
// If we are already in the desired mode, we don't need to do anything.
if (mode == timeMode)
return;
// Save the new mode.
timeMode = mode;
// Disable interrupts while we adjust the time information.
disable_interrupts (INT_CCP1);
switch (timeMode) {
case TIME_MODE_APRS:
timeRate = TIME_RATE_APRS;
timeInterruptCountRollOver = TIME_ROLLOVER_APRS;
break;
case TIME_MODE_PSK31:
timeRate = TIME_RATE_PSK31;
timeInterruptCountRollOver = TIME_ROLLOVER_PSK31;
break;
case TIME_MODE_HF_APRS:
timeRate = TIME_RATE_HF_APRS;
timeInterruptCountRollOver = TIME_ROLLOVER_HF_APRS;
break;
} // END switch
// If the current interrupt count is past the roller over state, move it back.
// This means we will loose an interrupt period, but we don't really care.
if (timeInterruptCount >= timeInterruptCountRollOver)
timeInterruptCount -= timeInterruptCountRollOver;
// Restart the interrupts and we are back on-line.
enable_interrupts (INT_CCP1);
}
// This function gets called every 1mS.
#INT_CCP1
void timeUpdate()
{
// Pin used to measure CPU load in interrupt.
output_high (PIN_B1);
// Setup the next interrupt for the operational mode.
timeCompare += timeRate;
CCP_1 = timeCompare;
// Call the appropriate time routine.
if (timeMode == TIME_MODE_PSK31)
psk31TimeUpdate();
else
tncTimeUpdate();
// Read the GPS serial port and save any incoming characters.
serialUpdate();
// Count the number of milliseconds required for the tenth second counter.
if (++timeInterruptCount == timeInterruptCountRollOver) {
timeInterruptCount = 0;
// This timer just ticks every 100mS and is used for general timing.
++timeTicks;
// Roll the counter over every second.
if (++time100mS == 10) {
time100mS = 0;
// We set this flag true every second.
timeUpdateFlag = true;
// Maintain a Real Time Clock.
if (++timeSeconds == 60) {
timeSeconds = 0;
if (++timeMinutes == 60) {
timeMinutes = 0;
++timeHours;
} // END if
} // END if
} // END if
// Flash the status LED at timeDutyCycle % per second. We use the duty cycle for mode feedback.
if (time100mS > timeDutyCycle)
output_low (IO_LED);
else
output_high (IO_LED);
} // END if
// Pin used to measure CPU load in interrupt.
output_low (PIN_B1);
}
// ****************************************************************************
// TNC
//
// The number of CCP_1 timer in each bit time.
#define TNC_BIT_RATE 384
// The number of start flag bytes to send before the packet message. (300mS)
#define TNC_TX_DELAY 45
// The UTC time in seconds to send an APRS packet.
#define TNC_TIMESLOT_1 54
#define TNC_TIMESLOT_2 56
#define TNC_TIMESLOT_3 58
// Modes for the TNC state machine.
#define TNC_TX_READY 0x00
#define TNC_TX_SYNC 0x01
#define TNC_TX_HEADER 0x02
#define TNC_TX_DATA 0x03
#define TNC_TX_END 0x04
// Modes for the packet state machine.
#define TNC_BOOT_MESSAGE 0x10
#define TNC_STATUS 0x11
#define TNC_GGA 0x12
#define TNC_RMC 0x13
// The size of the TNC output buffer.
#define TNC_BUFFER_SIZE 80
// The dwell duration in bits at each idle test tone.
#define TNC_DWELL_TIME 3600
// The packet header.
uint8_t TNC_AX25_HEADER[30] = {
'A' << 1, 'P' << 1, 'R' << 1, 'S' << 1, ' ' << 1, ' ' << 1, 0x60, \
'K' << 1, 'D' << 1, '7' << 1, 'L' << 1, 'M' << 1, 'O' << 1, 0x76, \
'G' << 1, 'A' << 1, 'T' << 1, 'E' << 1, ' ' << 1, ' ' << 1, 0x60, \
'W' << 1, 'I' << 1, 'D' << 1, 'E' << 1, '3' << 1, ' ' << 1, 0x67, \
0x03, 0xf0 };
uint8_t tncLastBit, tncMode, tncBitCount, tncShift, tncIndex, tncLength;
uint8_t tncBitStuff, *tncBufferPnt;
uint8_t tncPacketType, tncBuffer[TNC_BUFFER_SIZE];
// 1200 baud counter that determine which test signal to generate
uint16_t tncTestCount;
void tncInit()
{
tncLastBit = 0;
tncMode = TNC_TX_READY;
tncTestCount = 0;
tncPacketType = TNC_BOOT_MESSAGE;
}
/**
* Determine if the hardware if ready to transmit a 1200 baud packet.
*
* @return true if ready; otherwise false
*/
boolean tncIsFree()
{
if (tncMode == TNC_TX_READY)
return true;
return false;
}
// This ISR is called when the timer register matches the compare register.
// The interrupt rate is programmed to 833uS or 1 bit time at 1200 baud.
void tncTimeUpdate()
{
// Set the A-FSK frequency.
ddsAFSK ((tncLastBit == 0x00 ? false : true));
switch (tncMode) {
case TNC_TX_READY:
// Generate a test signal alteranting between high and low tones.
tncLastBit = (tncLastBit == 0 ? 1 : 0);
break;
case TNC_TX_SYNC:
// The variable tncShift contains the lastest data byte.
// NRZI enocde the data stream.
if ((tncShift & 0x01) == 0x00)
if (tncLastBit == 0)
tncLastBit = 1;
else
tncLastBit = 0;
// When the flag is done, determine if we need to send more or data.
if (++tncBitCount == 8) {
tncBitCount = 0;
tncShift = 0x7e;
// Once we transmit x mS of flags, send the data.
// txDelay bytes * 8 bits/byte * 833uS/bit = x mS
if (++tncIndex == TNC_TX_DELAY) {
tncIndex = 0;
tncShift = TNC_AX25_HEADER[0];
tncBitStuff = 0;
tncMode = TNC_TX_HEADER;
} // END if
} else
tncShift = tncShift >> 1;
break;
case TNC_TX_HEADER:
// Determine if we have sent 5 ones in a row, if we have send a zero.
if (tncBitStuff == 0x1f) {
if (tncLastBit == 0)
tncLastBit = 1;
else
tncLastBit = 0;
tncBitStuff = 0x00;
return;
} // END if
// The variable tncShift contains the lastest data byte.
// NRZI enocde the data stream.
if ((tncShift & 0x01) == 0x00)
if (tncLastBit == 0)
tncLastBit = 1;
else
tncLastBit = 0;
// Save the data stream so we can determine if bit stuffing is
// required on the next bit time.
tncBitStuff = ((tncBitStuff << 1) | (tncShift & 0x01)) & 0x1f;
// If all the bits were shifted, get the next byte.
if (++tncBitCount == 8) {
tncBitCount = 0;
// After the header is sent, then send the data.
if (++tncIndex == sizeof(TNC_AX25_HEADER)) {
tncIndex = 0;
tncShift = tncBuffer[0];
tncMode = TNC_TX_DATA;
} else
tncShift = TNC_AX25_HEADER[tncIndex];
} else
tncShift = tncShift >> 1;
break;
case TNC_TX_DATA:
// Determine if we have sent 5 ones in a row, if we have send a zero.
if (tncBitStuff == 0x1f) {
if (tncLastBit == 0)
tncLastBit = 1;
else
tncLastBit = 0;
tncBitStuff = 0x00;
return;
} // END if
// The variable tncShift contains the lastest data byte.
// NRZI enocde the data stream.
if ((tncShift & 0x01) == 0x00)
if (tncLastBit == 0)
tncLastBit = 1;
else
tncLastBit = 0;
// Save the data stream so we can determine if bit stuffing is
// required on the next bit time.
tncBitStuff = ((tncBitStuff << 1) | (tncShift & 0x01)) & 0x1f;
// If all the bits were shifted, get the next byte.
if (++tncBitCount == 8) {
tncBitCount = 0;
// If everything was sent, transmit closing flags.
if (++tncIndex == tncLength) {
tncIndex = 0;
tncShift = 0x7e;
tncMode = TNC_TX_END;
} else
tncShift = tncBuffer[tncIndex];
} else
tncShift = tncShift >> 1;
break;
case TNC_TX_END:
// The variable tncShift contains the lastest data byte.
// NRZI enocde the data stream.
if ((tncShift & 0x01) == 0x00)
if (tncLastBit == 0)
tncLastBit = 1;
else
tncLastBit = 0;
// If all the bits were shifted, get the next one.
if (++tncBitCount == 8) {
tncBitCount = 0;
tncShift = 0x7e;
// Transmit two closing flags.
if (++tncIndex == 2) {
tncMode = TNC_TX_READY;
// Turn off the DDSS and PA (PTT).
ddsPTT (false);
sysPAOutput (false);
return;
} // END if
} else
tncShift = tncShift >> 1;
break;
} // END switch
}
/**
* Write <b>value</b> to the TNC buffer. Maintain the pointer
* and length to the buffer. The pointer tncBufferPnt and tncLength
* must be set before calling this function for the first time.
*
* @param value to save to telemetry buffer
*/
void tncTxByte (uint8_t value)
{
*tncBufferPnt++ = value;
++tncLength;
}
/**
* Prepare an AX.25 data packet. Each time this method is called, it automatically
* rotates through 1 of 4 messages.
*/
void tncTxPacket()
{
uint8_t satCount;
uint16_t crc, altitude, temp;
// Only transmit if there is not another message in progress and PSK 31 isn't using the hardware.
if (tncMode != TNC_TX_READY || !psk31IsFree())
return;
// Set a pointer to our TNC output buffer.
tncBufferPnt = tncBuffer;
// Set the message length counter.
tncLength = 0;
// Determine the contents of the packet.
switch (tncPacketType) {
case TNC_BOOT_MESSAGE:
printf (tncTxByte, ">ANSR PSK31/APRS Beacon eXtreme - V1.03");
// Next packet to generate.
tncPacketType = TNC_STATUS;
break;
case TNC_STATUS:
// Get the GPS information to provide altitude and GPS tracking info.
if (gpsWaitMessage (GPS_GGA_MSG)) {
altitude = gpsParseAltitude();
temp = gpsParseDOP();
satCount = gpsParseSatCount();
} else
satCount = 255;
// Display the telemetry header.
printf (tncTxByte, ">Balloon ");
// Display the flight time.
printf (tncTxByte, "%02U:%02U:%02U ", timeHours, timeMinutes, timeSeconds);
// If satCount was set to 255, then we didn't receive GPS data.
if (satCount != 255) {
if (altitude != 0)
printf (tncTxByte, "%lu0' ", altitude);
else
printf (tncTxByte, "0' ");
printf (tncTxByte, "%lu.%ludop ", temp / 10, temp % 10);
printf (tncTxByte, "%02utrk ", satCount);
} else
printf (tncTxByte, "-----' -.-hdop --trk ");
// Print web address link.
printf (tncTxByte, "www.kd7lmo.net");
// Add <CR><LF> to make it the same as the GPS NMEA messages.
printf (tncTxByte, "\015");
// Next packet to generate.
tncPacketType = TNC_GGA;
gpsState = GPS_WAIT_MSG;
break;
case TNC_GGA:
if (gpsWaitMessage(GPS_GGA_MSG))
printf (tncTxByte, gpsBuffer);
else
printf (tncTxByte, ">Invalid GPS $GPGGA message");
tncPacketType = TNC_RMC;
gpsState = GPS_WAIT_MSG;
break;
case TNC_RMC:
if (gpsWaitMessage(GPS_RMC_MSG))
printf (tncTxByte, gpsBuffer);
else
printf (tncTxByte, ">Invalid GPS $GPRMC message");
// Next packet to generate.
tncPacketType = TNC_STATUS;
gpsState = GPS_WAIT_MSG;
break;
}
// Add the end of message character.
printf (tncTxByte, "\015");
// Calculate the CRC for the header and message.
crc = sysCRC16(TNC_AX25_HEADER, sizeof(TNC_AX25_HEADE
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -