Removed STM32 based modulation and moved task to PH of Si4464 (so stable HSE is not needed for STM32 anymore which was needed for radio modulation)

Fixed receiver value in decoder.py
Limited data display on decoder map to 14 days
Implemented additional exception handlers for bad internet connections
Modified SSDV/APRS protocol: removed redundant transmitted data
Modified SSDV/APRS protocol: Increased amount of data being sent in one packet
Adjusted clocks: (in order to save energy)
- Use HSI for STM32 only
- Use HSE for Si4464 only (HSE switched off, only switched on when Si4464 needs it)
- Reduced STM32 clock to 3MHz
Improved Morse implementation
Fixed thread (module) startup delay bug
Changed behavior of Watchdog LED: Flashing instead of blinking (in order to save energy)
Fixed preamble bug from AX25 implementation
master
Sven Steudte 2017-10-14 05:58:51 +02:00
rodzic af870d89e0
commit f6d69c9afe
18 zmienionych plików z 564 dodań i 549 usunięć

Wyświetl plik

@ -12,7 +12,7 @@ import position
# Parse arguments from terminal
parser = argparse.ArgumentParser(description='APRS/SSDV decoder')
parser.add_argument('-c', '--call', help='Callsign of the station', required=True)
parser.add_argument('-c', '--call', help='Callsign of the station', default='N0CALL')
parser.add_argument('-n', '--grouping', help='Amount packets that will be sent to the SSDV server in one request', default=1, type=int)
parser.add_argument('-d', '--device', help='Serial device (\'-\' for stdin)', default='-')
parser.add_argument('-b', '--baudrate', help='Baudrate for serial device', default=9600, type=int)
@ -30,7 +30,7 @@ sqlite.cursor().execute("""
lat FLOAT,
lon FLOAT,
alt INTEGER,
new INTEGER,
isnew INTEGER,
comment TEXT,
sequ INTEGER,
tel1 INTEGER,
@ -61,11 +61,6 @@ sqlite.cursor().execute("""
""" Packet handler for received APRS packets"""
def received_data(data):
# Debug
print('====================================================================================================')
print(data)
print('----------------------------------------------------------------------------------------------------')
# Parse line and detect data
# Position (.*)\>APECAN(.*?):\/([0-9]{6}h)(.{13})(.*?)\|(.*)\|
# Image (.*)\>APECAN(.*?):\/([0-9]{6}h)(.{13})I(.*)
@ -76,8 +71,14 @@ def received_data(data):
dat = re.search("(.*)\>APECAN(.*?):\/([0-9]{6}h)(.{13})(I|J|L)(.*)", data)
if all:
call = all.group(1)
# Debug
print('='*100)
print(data)
print('-'*100)
call = all.group(1).split(' ')[-1]
rxer = all.group(2).split(',')[-1]
if not len(rxer): rxer = args.call
tim = all.group(3)
posi = all.group(4)

Wyświetl plik

@ -32,6 +32,8 @@ class MyDB extends SQLite3 {
WHERE position.call = :call
AND position.lat != 0
AND position.lon != 0
AND position.isnew = 1
AND position.time + 86400*14 > CAST(strftime('%s', 'now') as DECIMAL)
GROUP BY position.call,position.time
ORDER BY position.time ASC
");
@ -45,4 +47,3 @@ class MyDB extends SQLite3 {
}
}
?>

Wyświetl plik

@ -41,18 +41,17 @@ data - Binary data
def insert_image(sqlite, receiver, call, tim, posi, data, typ, server, grouping):
global jsons
if len(data) != 110:
return # APRS message sampled too short
if not (typ is 'I' and len(data) == 125) and not (typ is 'J' and len(data) == 124):
return # APRS message has invalid type or length (or both)
# Decode various meta data
timd,x,y,z,teld,new = decode_position(tim, posi, None)
imageID = data[0]
packetID = (data[1] << 8) | data[2]
data = binascii.hexlify(data[3:]).decode("ascii")
print(len(data))
# Debug
print('Received packet from %s image %d packet %d' % (call, imageID, packetID))
print('Received %s-Packet Image %d Packet %d' % (typ, imageID, packetID))
# Insert
sqlite.cursor().execute("""
@ -75,7 +74,7 @@ def insert_image(sqlite, receiver, call, tim, posi, data, typ, server, grouping)
else:
bcall = bcall[0][0:6] # Callsign has 6 chars, so take the call without SSID
data = '66%08x' % encode_callsign(bcall) + data
data = ('67%08x%02x%04x' % (encode_callsign(bcall), imageID, packetID)) + data
sqlite.cursor().execute("""
UPDATE image
@ -101,7 +100,6 @@ def insert_image(sqlite, receiver, call, tim, posi, data, typ, server, grouping)
sqlite.commit()
# Get both data entries
cur = sqlite.cursor()
cur.execute("""
@ -136,7 +134,7 @@ def insert_image(sqlite, receiver, call, tim, posi, data, typ, server, grouping)
# SSDV decode
cur = sqlite.cursor()
cur.execute("SELECT GROUP_CONCAT('55' || data1 || data2 || crc || '0000000000000000000000000000000000000000000000000000000000000000', '') FROM image WHERE call = ? AND imageID = ? AND time = ? GROUP BY imageID ORDER BY packetID", (call, imageID, int(timd.timestamp())))
cur.execute("SELECT GROUP_CONCAT('55' || data1 || data2 || crc, '') FROM image WHERE call = ? AND imageID = ? AND time = ? GROUP BY imageID ORDER BY packetID", (call, imageID, int(timd.timestamp())))
name = 'html/images/%s-%d-%d.jpg' % (call.replace('-',''), int(timd.timestamp()), imageID)
f = open(name, 'wb')
process = Popen(['./ssdv', '-d'], stdin=PIPE, stdout=f, stderr=PIPE)
@ -145,7 +143,7 @@ def insert_image(sqlite, receiver, call, tim, posi, data, typ, server, grouping)
f.close()
# Create message for SSDV server (and save to array)
ssdv = '55' + data + ('%08x' % crc) + (64*'0')
ssdv = '55' + data + ('%08x' % crc)
jsons.append("""{
\"type\": \"packet\",
\"packet\": \"""" + ssdv + """\",
@ -168,11 +166,13 @@ def insert_image(sqlite, receiver, call, tim, posi, data, typ, server, grouping)
print('Send to SSDV data server')
try:
result = urllib.request.urlopen(req, "".join(json.split(' ')).encode("ascii")) # Send packets to server
print('Response from Server: OK')
print('Response from SSDV-Server: OK')
err = False
except urllib.error.URLError as error:
if error.code == 400:
print('Response from Server: %s', error.read())
if not hasattr(error, 'code'): # (Bug in urllib) most likely network not available
print('Error: Could not connect to SSDV-Server')
elif error.code == 400:
print('Response from SSDV-Server: %s' % error.read().decode('ascii').replace('\n',''))
err = False
else:
print('SSDV-Server connection error... try again')

Wyświetl plik

@ -18,7 +18,7 @@ def insert_log(sqlite, call, data):
tim_stringified = datetime.utcfromtimestamp(tim).strftime("%Y-%m-%d %H:%M:%S")
try:
sqlite.cursor().execute("INSERT OR FAIL INTO position (call,time,org,lat,lon,alt) VALUES (?,?,'log',?,?,?)", (call, tim, lat, lon, alt))
sqlite.cursor().execute("INSERT OR FAIL INTO position (call,time,org,lat,lon,alt,isnew) VALUES (?,?,'log',?,?,?,1)", (call, tim, lat, lon, alt))
print("Decoded log from %s time %s => lat=%06.3f lon=%07.3f alt=%05d" % (call, tim_stringified, lat, lon, alt))
except sqlite3.IntegrityError:
print("Decoded log from %s time %s => lat=%06.3f lon=%07.3f alt=%05d already in db" % (call, tim_stringified, lat, lon, alt))
@ -56,7 +56,7 @@ def decode_position(tim, posi, tel):
timd -= timedelta(1)
# Decode GPS Fix Type
new = ((ord(posi[12])-33) >> 5) & 0x1
isnew = ((ord(posi[12])-33) >> 5) & 0x1
# Decode telemetry
teld = [0]*6
@ -66,22 +66,22 @@ def decode_position(tim, posi, tel):
t0 = ord(tel[i*2+1]) - 33
teld.append(t0 + t1*91)
return timd,x,y,z,teld,new
return timd,x,y,z,teld,isnew
def insert_position(sqlite, call, tim, posi, comm, tel): #sqlite, call, data
# Decode
timd,x,y,z,teld,new = decode_position(tim, posi, tel)
timd,x,y,z,teld,isnew = decode_position(tim, posi, tel)
# Insert
sqlite.cursor().execute("""
INSERT OR REPLACE INTO position (call,time,org,lat,lon,alt,new,comment,sequ,tel1,tel2,tel3,tel4,tel5)
INSERT OR REPLACE INTO position (call,time,org,lat,lon,alt,isnew,comment,sequ,tel1,tel2,tel3,tel4,tel5)
VALUES (?,?,'pos',?,?,?,?,?,?,?,?,?,?,?)""",
(call, int(timd.timestamp()), y, x, int(z), new, comm, teld[0], teld[1], teld[2], teld[3], teld[4], teld[5])
(call, int(timd.timestamp()), y, x, int(z), isnew, comm, teld[0], teld[1], teld[2], teld[3], teld[4], teld[5])
)
sqlite.commit()
# Debug
tim_stringified = timd.strftime("%Y-%m-%d %H:%M:%S")
print("Decoded position from %s time %s => lat=%f lon=%f alt=%d new=%d comment=%s, sequ=%d tel=[%d,%d,%d,%d,%d]"
% (call, tim_stringified, y, x, int(z), new, comm, teld[0], teld[1], teld[2], teld[3], teld[4], teld[5]))
% (call, tim_stringified, y, x, int(z), isnew, comm, teld[0], teld[1], teld[2], teld[3], teld[4], teld[5]))

BIN
decoder/ssdv 100755

Plik binarny nie jest wyświetlany.

Wyświetl plik

@ -397,7 +397,7 @@ void start_user_modules(void)
config[0].aprs_conf.tel_enc = TRUE; // Transmit Telemetry encoding information activated
config[0].aprs_conf.tel_enc_cycle = 3600; // Transmit Telemetry encoding information every 3600sec
chsnprintf(config[0].aprs_conf.tel_comment, 30, "http://ssdv.habhub.org/DL7AD");// Telemetry comment
start_position_thread(&config[0]);
//start_position_thread(&config[0]);
// Module POSITION, UKHAS 2m 2FSK
config[1].power = 127; // Transmission Power
@ -407,7 +407,7 @@ void start_user_modules(void)
config[1].trigger.type = TRIG_CONTINUOUSLY; // Transmit continuously
config[1].fsk_conf.bits = 8; // 8 bit
config[1].fsk_conf.stopbits = 2; // 2 stopbits
config[1].fsk_conf.predelay = 1000; // Preamble (1000ms)
config[1].fsk_conf.predelay = 3000; // Preamble (1000ms)
config[1].fsk_conf.baud = 50; // Baudrate
config[1].fsk_conf.shift = 425; // Frequency shift in Hz
chsnprintf(config[1].ukhas_conf.callsign, 16, "DL7AD"); // UKHAS Callsign
@ -446,7 +446,7 @@ void start_user_modules(void)
config[3].ssdv_conf.res = RES_QVGA; // Resolution QVGA
config[3].ssdv_conf.redundantTx = true; // Redundant transmission (transmit packets twice)
config[3].ssdv_conf.quality = 4; // Image quality
start_image_thread(&config[3]);
//start_image_thread(&config[3]);
// Module IMAGE, APRS 2m 2GFSK
config[4].power = 127; // Transmission Power
@ -473,9 +473,9 @@ void start_user_modules(void)
config[5].trigger.type = TRIG_CONTINUOUSLY; // Transmit continuously
config[5].fsk_conf.bits = 8; // 8bit
config[5].fsk_conf.stopbits = 2; // 2 Stopbits
config[5].fsk_conf.predelay = 1000; // Preamble (1000ms)
config[5].fsk_conf.baud = 600; // Baudrate (600baud)
config[5].fsk_conf.shift = 1000; // Frequency shift (1000Hz)
config[5].fsk_conf.predelay = 3000; // Preamble (1000ms)
config[5].fsk_conf.baud = 300; // Baudrate (600baud)
config[5].fsk_conf.shift = 425; // Frequency shift (1000Hz)
chsnprintf(config[5].ssdv_conf.callsign, 7, "DL7AD"); // SSDV Callsign
config[5].ssdv_conf.ram_buffer = ssdv_buffer; // Camera buffer
config[5].ssdv_conf.ram_size = sizeof(ssdv_buffer); // Buffer size
@ -499,7 +499,6 @@ void start_user_modules(void)
config[6].aprs_conf.symbol = SYM_BALLOON; // APRS Symbol
chsnprintf(config[6].aprs_conf.path, 16, "WIDE1-1"); // APRS Path
config[6].aprs_conf.preamble = 300; // APRS Preamble (300ms)
start_logging_thread(&config[6]);
//start_logging_thread(&config[6]);
}

Wyświetl plik

@ -27,18 +27,20 @@ bool initialized = false;
* @param mv Oscillator voltage in mv
*/
void Si4464_Init(void) {
// Reset radio)
Si4464_shutdown();
chThdSleepMilliseconds(10);
// Configure SPI pins
// Configure Radio pins
palSetLineMode(LINE_SPI_SCK, PAL_MODE_ALTERNATE(6) | PAL_STM32_OSPEED_HIGHEST); // SCK
palSetLineMode(LINE_SPI_MISO, PAL_MODE_ALTERNATE(6) | PAL_STM32_OSPEED_HIGHEST); // MISO
palSetLineMode(LINE_SPI_MOSI, PAL_MODE_ALTERNATE(6) | PAL_STM32_OSPEED_HIGHEST); // MOSI
palSetLineMode(LINE_RADIO_CS, PAL_MODE_OUTPUT_PUSHPULL | PAL_STM32_OSPEED_HIGHEST); // RADIO CS
palSetLineMode(LINE_RADIO_SDN, PAL_MODE_OUTPUT_PUSHPULL); // RADIO SDN
palSetLineMode(LINE_OSC_EN, PAL_MODE_OUTPUT_PUSHPULL); // Oscillator
palSetLine(LINE_RADIO_CS);
// Reset radio
Si4464_shutdown();
palSetLine(LINE_OSC_EN); // Activate Oscillator
chThdSleepMilliseconds(10);
// Power up transmitter
palClearLine(LINE_RADIO_SDN); // Radio SDN low (power up transmitter)
chThdSleepMilliseconds(10); // Wait for transmitter to power up
@ -52,6 +54,55 @@ void Si4464_Init(void) {
Si4464_write(init_command, 7);
chThdSleepMilliseconds(25);
// Set transmitter GPIOs
uint8_t gpio_pin_cfg_command[] = {
0x13, // Command type = GPIO settings
0x00, // GPIO0 0 - PULL_CTL[1bit] - GPIO_MODE[6bit]
0x23, // GPIO1 0 - PULL_CTL[1bit] - GPIO_MODE[6bit]
0x00, // GPIO2 0 - PULL_CTL[1bit] - GPIO_MODE[6bit]
0x00, // GPIO3 0 - PULL_CTL[1bit] - GPIO_MODE[6bit]
0x00, // NIRQ
0x00, // SDO
0x00 // GEN_CONFIG
};
Si4464_write(gpio_pin_cfg_command, 8);
chThdSleepMilliseconds(25);
// Set FIFO empty interrupt threshold (32 byte)
uint8_t set_fifo_irq[] = {0x11, 0x12, 0x01, 0x0B, 0x20};
Si4464_write(set_fifo_irq, 5);
// Set FIFO to 129 byte
uint8_t set_129byte[] = {0x11, 0x00, 0x01, 0x03, 0x10};
Si4464_write(set_129byte, 5);
// Reset FIFO
uint8_t reset_fifo[] = {0x15, 0x01};
Si4464_write(reset_fifo, 2);
uint8_t unreset_fifo[] = {0x15, 0x00};
Si4464_write(unreset_fifo, 2);
// Disable preamble
uint8_t disable_preamble[] = {0x11, 0x10, 0x01, 0x00, 0x00};
Si4464_write(disable_preamble, 5);
// Do not transmit sync word
uint8_t no_sync_word[] = {0x11, 0x11, 0x01, 0x00, (0x01 << 7)};
Si4464_write(no_sync_word, 5);
// Setup the NCO modulo and oversampling mode
uint32_t s = RADIO_CLK / 10;
uint8_t f3 = (s >> 24) & 0xFF;
uint8_t f2 = (s >> 16) & 0xFF;
uint8_t f1 = (s >> 8) & 0xFF;
uint8_t f0 = (s >> 0) & 0xFF;
uint8_t setup_oversampling[] = {0x11, 0x20, 0x04, 0x06, f3, f2, f1, f0};
Si4464_write(setup_oversampling, 8);
// transmit LSB first
uint8_t use_lsb_first[] = {0x11, 0x12, 0x01, 0x06, 0x01};
Si4464_write(use_lsb_first, 5);
// Temperature readout
TRACE_INFO("SI > Transmitter temperature %d degC", Si4464_getTemperature());
initialized = true;
@ -173,44 +224,12 @@ void setShift(uint16_t shift) {
}
void setModemAFSK(void) {
// Set transmitter GPIOs
palSetLineMode(LINE_RADIO_GPIO, PAL_MODE_OUTPUT_PUSHPULL); // GPIO1
uint8_t gpio_pin_cfg_command[] = {
0x13, // Command type = GPIO settings
0x00, // GPIO0 0 - PULL_CTL[1bit] - GPIO_MODE[6bit]
0x44, // GPIO1 0 - PULL_CTL[1bit] - GPIO_MODE[6bit]
0x00, // GPIO2 0 - PULL_CTL[1bit] - GPIO_MODE[6bit]
0x00, // GPIO3 0 - PULL_CTL[1bit] - GPIO_MODE[6bit]
0x00, // NIRQ
0x00, // SDO
0x00 // GEN_CONFIG
};
Si4464_write(gpio_pin_cfg_command, 8);
chThdSleepMilliseconds(25);
// Disable preamble
uint8_t disable_preamble[] = {0x11, 0x10, 0x01, 0x00, 0x00};
Si4464_write(disable_preamble, 5);
// Do not transmit sync word
uint8_t no_sync_word[] = {0x11, 0x11, 0x01, 0x00, (0x01 << 7)};
Si4464_write(no_sync_word, 5);
// Setup the NCO modulo and oversampling mode
uint32_t s = RADIO_CLK / 10;
uint8_t f3 = (s >> 24) & 0xFF;
uint8_t f2 = (s >> 16) & 0xFF;
uint8_t f1 = (s >> 8) & 0xFF;
uint8_t f0 = (s >> 0) & 0xFF;
uint8_t setup_oversampling[] = {0x11, 0x20, 0x04, 0x06, f3, f2, f1, f0};
Si4464_write(setup_oversampling, 8);
// Setup the NCO data rate for APRS
uint8_t setup_data_rate[] = {0x11, 0x20, 0x03, 0x03, 0x00, 0x11, 0x30};
uint8_t setup_data_rate[] = {0x11, 0x20, 0x03, 0x03, 0x00, 0x33, 0x90};
Si4464_write(setup_data_rate, 7);
// Use 2GFSK from async GPIO1
uint8_t use_2gfsk[] = {0x11, 0x20, 0x01, 0x00, 0x2B};
// Use 2GFSK from FIFO (PH)
uint8_t use_2gfsk[] = {0x11, 0x20, 0x01, 0x00, 0x03};
Si4464_write(use_2gfsk, 5);
// Set AFSK filter
@ -222,109 +241,35 @@ void setModemAFSK(void) {
}
}
void setModemOOK(void) {
// Set transmitter GPIOs
palSetLineMode(LINE_RADIO_GPIO, PAL_MODE_OUTPUT_PUSHPULL); // GPIO1
uint8_t gpio_pin_cfg_command[] = {
0x13, // Command type = GPIO settings
0x00, // GPIO0 0 - PULL_CTL[1bit] - GPIO_MODE[6bit]
0x44, // GPIO1 0 - PULL_CTL[1bit] - GPIO_MODE[6bit]
0x00, // GPIO2 0 - PULL_CTL[1bit] - GPIO_MODE[6bit]
0x00, // GPIO3 0 - PULL_CTL[1bit] - GPIO_MODE[6bit]
0x00, // NIRQ
0x00, // SDO
0x00 // GEN_CONFIG
};
Si4464_write(gpio_pin_cfg_command, 8);
chThdSleepMilliseconds(25);
void setModemOOK(ook_conf_t* conf) {
// Setup the NCO data rate for 2FSK
uint16_t speed = conf->speed * 5 / 6;
uint8_t setup_data_rate[] = {0x11, 0x20, 0x03, 0x03, 0x00, 0x00, (uint8_t)speed};
Si4464_write(setup_data_rate, 7);
// Use OOK from async GPIO1
uint8_t use_ook[] = {0x11, 0x20, 0x01, 0x00, 0xA9};
// Use 2FSK from FIFO (PH)
uint8_t use_ook[] = {0x11, 0x20, 0x01, 0x00, 0x01};
Si4464_write(use_ook, 5);
}
void setModem2FSK(void) {
// Set transmitter GPIOs
palSetLineMode(LINE_RADIO_GPIO, PAL_MODE_OUTPUT_PUSHPULL); // GPIO1
uint8_t gpio_pin_cfg_command[] = {
0x13, // Command type = GPIO settings
0x00, // GPIO0 0 - PULL_CTL[1bit] - GPIO_MODE[6bit]
0x44, // GPIO1 0 - PULL_CTL[1bit] - GPIO_MODE[6bit]
0x00, // GPIO2 0 - PULL_CTL[1bit] - GPIO_MODE[6bit]
0x00, // GPIO3 0 - PULL_CTL[1bit] - GPIO_MODE[6bit]
0x00, // NIRQ
0x00, // SDO
0x00 // GEN_CONFIG
};
Si4464_write(gpio_pin_cfg_command, 8);
chThdSleepMilliseconds(25);
void setModem2FSK(fsk_conf_t* conf) {
// Setup the NCO data rate for 2FSK
uint8_t setup_data_rate[] = {0x11, 0x20, 0x03, 0x03, (uint8_t)(conf->baud >> 16), (uint8_t)(conf->baud >> 8), (uint8_t)conf->baud};
Si4464_write(setup_data_rate, 7);
// Initialize high tone
RADIO_WRITE_GPIO(HIGH);
// use 2FSK from async GPIO1
uint8_t use_2fsk[] = {0x11, 0x20, 0x01, 0x00, 0xAA};
// Use 2FSK from FIFO (PH)
uint8_t use_2fsk[] = {0x11, 0x20, 0x01, 0x00, 0x02};
Si4464_write(use_2fsk, 5);
}
void setModem2GFSK(gfsk_conf_t* conf) {
// Set transmitter GPIOs
palSetLineMode(LINE_RADIO_GPIO, PAL_MODE_INPUT); // GPIO1
uint8_t gpio_pin_cfg_command[] = {
0x13, // Command type = GPIO settings
0x00, // GPIO0 0 - PULL_CTL[1bit] - GPIO_MODE[6bit]
0x23, // GPIO1 0 - PULL_CTL[1bit] - GPIO_MODE[6bit]
0x00, // GPIO2 0 - PULL_CTL[1bit] - GPIO_MODE[6bit]
0x00, // GPIO3 0 - PULL_CTL[1bit] - GPIO_MODE[6bit]
0x00, // NIRQ
0x00, // SDO
0x00 // GEN_CONFIG
};
Si4464_write(gpio_pin_cfg_command, 8);
chThdSleepMilliseconds(25);
// Set FIFO empty interrupt threshold (32 byte)
uint8_t set_fifo_irq[] = {0x11, 0x12, 0x01, 0x0B, 0x20};
Si4464_write(set_fifo_irq, 5);
// Set FIFO to 129 byte
uint8_t set_129byte[] = {0x11, 0x00, 0x01, 0x03, 0x10};
Si4464_write(set_129byte, 5);
// Reset FIFO
uint8_t reset_fifo[] = {0x15, 0x01};
Si4464_write(reset_fifo, 2);
uint8_t unreset_fifo[] = {0x15, 0x00};
Si4464_write(unreset_fifo, 2);
// Disable preamble
uint8_t disable_preamble[] = {0x11, 0x10, 0x01, 0x00, 0x00};
Si4464_write(disable_preamble, 5);
// Do not transmit sync word
uint8_t no_sync_word[] = {0x11, 0x11, 0x01, 0x00, (0x01 << 7)};
Si4464_write(no_sync_word, 5);
// Setup the NCO modulo and oversampling mode
uint32_t s = RADIO_CLK / 10;
uint8_t f3 = (s >> 24) & 0xFF;
uint8_t f2 = (s >> 16) & 0xFF;
uint8_t f1 = (s >> 8) & 0xFF;
uint8_t f0 = (s >> 0) & 0xFF;
uint8_t setup_oversampling[] = {0x11, 0x20, 0x04, 0x06, f3, f2, f1, f0};
Si4464_write(setup_oversampling, 8);
// Setup the NCO data rate for 2GFSK
uint8_t setup_data_rate[] = {0x11, 0x20, 0x03, 0x03, (uint8_t)(conf->speed >> 16), (uint8_t)(conf->speed >> 8), (uint8_t)conf->speed};
Si4464_write(setup_data_rate, 7);
// Use 2GFSK from FIFO (PH)
uint8_t use_2gfsk[] = {0x11, 0x20, 0x01, 0x00, 0x23};
uint8_t use_2gfsk[] = {0x11, 0x20, 0x01, 0x00, 0x03};
Si4464_write(use_2gfsk, 5);
// transmit LSB first
uint8_t use_lsb_first[] = {0x11, 0x12, 0x01, 0x06, 0x01};
Si4464_write(use_lsb_first, 5);
}
void setPowerLevel(int8_t level) {
@ -350,7 +295,8 @@ void stopTx(void) {
void Si4464_shutdown(void) {
palSetLine(LINE_RADIO_SDN); // Power down chip
palSetLine(LINE_IO_LED1); // Set indication LED
RADIO_WRITE_GPIO(false); // Set GPIO1 low
palClearLine(LINE_OSC_EN); // Shutdown oscillator
RADIO_WRITE_GPIO(false); // Set GPIO1 low
initialized = false;
}
@ -416,3 +362,4 @@ int8_t Si4464_getTemperature(void) {
bool isRadioInitialized(void) {
return initialized;
}

Wyświetl plik

@ -27,8 +27,8 @@ void Si4464_write(uint8_t* txData, uint32_t len);
void setFrequency(uint32_t freq, uint16_t shift);
void setShift(uint16_t shift);
void setModemAFSK(void);
void setModemOOK(void);
void setModem2FSK(void);
void setModemOOK(ook_conf_t* conf);
void setModem2FSK(fsk_conf_t* conf);
void setModem2GFSK(gfsk_conf_t* conf);
void setDeviation(uint32_t deviation);
void setPowerLevel(int8_t level);

Wyświetl plik

@ -55,11 +55,13 @@ int main(void) {
start_user_modules(); // Startup optional modules (eg. POSITION, LOG, ...)
while(true) {
#if ACTIVATE_USB
if(SDU1.config->usbp->state == USB_ACTIVE) {
thread_t *shelltp = chThdCreateFromHeap(NULL, THD_WORKING_AREA_SIZE(512), "shell", NORMALPRIO+1, shellThread, (void*)&shell_cfg);
chThdWait(shelltp);
}
chThdSleepMilliseconds(1000);
#endif
chThdSleepMilliseconds(10000);
}
}

Wyświetl plik

@ -39,21 +39,21 @@
* HAL driver system settings.
*/
#define STM32_NO_INIT FALSE
#define STM32_HSI_ENABLED FALSE
#define STM32_HSI_ENABLED TRUE
#define STM32_LSI_ENABLED TRUE
#define STM32_HSE_ENABLED TRUE
#define STM32_HSE_ENABLED FALSE
#define STM32_LSE_ENABLED FALSE
#define STM32_CLOCK48_REQUIRED TRUE
#define STM32_SW STM32_SW_PLL
#define STM32_PLLSRC STM32_PLLSRC_HSE
#define STM32_PLLM_VALUE 26
#define STM32_PLLSRC STM32_PLLSRC_HSI
#define STM32_PLLM_VALUE 16
#define STM32_PLLN_VALUE 192
#define STM32_PLLP_VALUE 4
#define STM32_PLLQ_VALUE 4
#if ACTIVATE_USB /* Activate 48MHz when USB is activated, otherwise 6MHz */
#define STM32_HPRE STM32_HPRE_DIV1
#else
#define STM32_HPRE STM32_HPRE_DIV8
#define STM32_HPRE STM32_HPRE_DIV16
#endif
#define STM32_PPRE1 STM32_PPRE1_DIV1
#define STM32_PPRE2 STM32_PPRE2_DIV1

Wyświetl plik

@ -120,7 +120,7 @@ void ax25_init(ax25_t *packet)
void ax25_send_header(ax25_t *packet, const char *callsign, uint8_t ssid, const char *path, uint16_t preamble)
{
uint8_t i, j;
uint16_t i, j;
uint8_t tmp[8];
packet->ones_in_a_row = 0;
packet->crc = 0xffff;

Wyświetl plik

@ -1,265 +1,259 @@
#include "ch.h"
#include "hal.h"
#include "morse.h"
#include "debug.h"
#include <stdint.h>
#include <string.h>
static uint8_t *buffer;
static uint32_t c;
#define ADDB(bit) { \
#define ADDB(buffer, bit) { \
buffer[c/8] |= ((bit & 0x1) << (c % 8)); \
c++; \
}
void dah(void)
void dah(uint8_t *buffer)
{
ADDB(1);
ADDB(1);
ADDB(1);
ADDB(0);
ADDB(buffer, 1);
ADDB(buffer, 1);
ADDB(buffer, 1);
ADDB(buffer, 0);
}
void dit(void)
void dit(uint8_t *buffer)
{
ADDB(1);
ADDB(0);
ADDB(buffer, 1);
ADDB(buffer, 0);
}
void blank(uint32_t ticks) {
void blank(uint8_t *buffer, uint32_t ticks) {
for(uint32_t i=0; i<ticks; i++)
ADDB(0);
ADDB(buffer, 0);
}
void morse_encode_char(char letter)
void morse_encode_char(uint8_t *buffer, char letter)
{
switch(letter) {
case 'A':
dit();
dah();
dit(buffer);
dah(buffer);
break;
case 'B':
dah();
dit();
dit();
dit();
dah(buffer);
dit(buffer);
dit(buffer);
dit(buffer);
break;
case 'C':
dah();
dit();
dah();
dit();
dah(buffer);
dit(buffer);
dah(buffer);
dit(buffer);
break;
case 'D':
dah();
dit();
dit();
dah(buffer);
dit(buffer);
dit(buffer);
break;
case 'E':
dit();
dit(buffer);
break;
case 'F':
dit();
dit();
dah();
dit();
dit(buffer);
dit(buffer);
dah(buffer);
dit(buffer);
break;
case 'G':
dah();
dah();
dit();
dah(buffer);
dah(buffer);
dit(buffer);
break;
case 'H':
dit();
dit();
dit();
dit();
dit(buffer);
dit(buffer);
dit(buffer);
dit(buffer);
break;
case 'I':
dit();
dit();
dit(buffer);
dit(buffer);
break;
case 'J':
dit();
dah();
dah();
dah();
dit(buffer);
dah(buffer);
dah(buffer);
dah(buffer);
break;
case 'K':
dah();
dit();
dah();
dah(buffer);
dit(buffer);
dah(buffer);
break;
case 'L':
dit();
dah();
dit();
dit();
dit(buffer);
dah(buffer);
dit(buffer);
dit(buffer);
break;
case 'M':
dah();
dah();
dah(buffer);
dah(buffer);
break;
case 'N':
dah();
dit();
dah(buffer);
dit(buffer);
break;
case 'O':
dah();
dah();
dah();
dah(buffer);
dah(buffer);
dah(buffer);
break;
case 'P':
dit();
dah();
dah();
dit();
dit(buffer);
dah(buffer);
dah(buffer);
dit(buffer);
break;
case 'Q':
dah();
dah();
dit();
dah();
dah(buffer);
dah(buffer);
dit(buffer);
dah(buffer);
break;
case 'R':
dit();
dah();
dit();
dit(buffer);
dah(buffer);
dit(buffer);
break;
case 'S':
dit();
dit();
dit();
dit(buffer);
dit(buffer);
dit(buffer);
break;
case 'T':
dah();
dah(buffer);
break;
case 'U':
dit();
dit();
dah();
dit(buffer);
dit(buffer);
dah(buffer);
break;
case 'V':
dit();
dit();
dit();
dah();
dit(buffer);
dit(buffer);
dit(buffer);
dah(buffer);
break;
case 'W':
dit();
dah();
dah();
dit(buffer);
dah(buffer);
dah(buffer);
break;
case 'X':
dah();
dit();
dit();
dah();
dah(buffer);
dit(buffer);
dit(buffer);
dah(buffer);
break;
case 'Y':
dah();
dit();
dah();
dah();
dah(buffer);
dit(buffer);
dah(buffer);
dah(buffer);
break;
case 'Z':
dah();
dah();
dit();
dit();
dah(buffer);
dah(buffer);
dit(buffer);
dit(buffer);
break;
case '1':
dit();
dah();
dah();
dah();
dah();
dit(buffer);
dah(buffer);
dah(buffer);
dah(buffer);
dah(buffer);
break;
case '2':
dit();
dit();
dah();
dah();
dah();
dit(buffer);
dit(buffer);
dah(buffer);
dah(buffer);
dah(buffer);
break;
case '3':
dit();
dit();
dit();
dah();
dah();
dit(buffer);
dit(buffer);
dit(buffer);
dah(buffer);
dah(buffer);
break;
case '4':
dit();
dit();
dit();
dit();
dah();
dit(buffer);
dit(buffer);
dit(buffer);
dit(buffer);
dah(buffer);
break;
case '5':
dit();
dit();
dit();
dit();
dit();
dit(buffer);
dit(buffer);
dit(buffer);
dit(buffer);
dit(buffer);
break;
case '6':
dah();
dit();
dit();
dit();
dit();
dah(buffer);
dit(buffer);
dit(buffer);
dit(buffer);
dit(buffer);
break;
case '7':
dah();
dah();
dit();
dit();
dit();
dah(buffer);
dah(buffer);
dit(buffer);
dit(buffer);
dit(buffer);
break;
case '8':
dah();
dah();
dah();
dit();
dit();
dah(buffer);
dah(buffer);
dah(buffer);
dit(buffer);
dit(buffer);
break;
case '9':
dah();
dah();
dah();
dah();
dit();
dah(buffer);
dah(buffer);
dah(buffer);
dah(buffer);
dit(buffer);
break;
case '0':
dah();
dah();
dah();
dah();
dah();
dah(buffer);
dah(buffer);
dah(buffer);
dah(buffer);
dah(buffer);
break;
case ' ':
blank(3);
blank(buffer, 3);
break;
case '.':
dit();
dah();
dit();
dah();
dit();
dah();
dit(buffer);
dah(buffer);
dit(buffer);
dah(buffer);
dit(buffer);
dah(buffer);
break;
}
blank(4);
blank(buffer, 4);
}
uint32_t morse_encode(uint8_t* data, const char* letter)
uint32_t morse_encode(uint8_t* buffer, uint32_t length, const char* in)
{
// Blanking bits TODO: Replace this
for(uint32_t i=0; i<256; i++)
data[i] = 0;
memset(buffer, 0, length); // Tidy up
c = 0; // Bitlength
// Encode morse
buffer = data; // Buffer
c = 0; // Bitlength
for(uint32_t i=0; letter[i]!=0; i++)
morse_encode_char(letter[i]);
for(uint32_t i=0; in[i] != 0 && c < length*8-16; i++)
morse_encode_char(buffer, in[i]);
return c;
}

Wyświetl plik

@ -4,7 +4,7 @@
#include "ch.h"
#include "hal.h"
uint32_t morse_encode(uint8_t* data, const char* letter);
uint32_t morse_encode(uint8_t* buffer, uint32_t length, const char* in);
#endif

Wyświetl plik

@ -11,7 +11,7 @@
#include <string.h>
// APRS related
#define PLAYBACK_RATE ((STM32_PCLK1) / 500) /* Samples per second (48Mhz / 250 = 192kHz) */
#define PLAYBACK_RATE 13200
#define BAUD_RATE 1200 /* APRS AFSK baudrate */
#define SAMPLES_PER_BAUD (PLAYBACK_RATE / BAUD_RATE) /* Samples per baud (192kHz / 1200baud = 160samp/baud) */
#define PHASE_DELTA_1200 (((2 * 1200) << 16) / PLAYBACK_RATE) /* Delta-phase per sample for 1200Hz tone */
@ -49,32 +49,191 @@ static void initAFSK(void) {
active_mod = MOD_AFSK;
}
static void sendAFSK(void) {
uint8_t getAFSKbyte(void)
{
if(packet_pos == radio_msg.bin_len) // Packet transmission finished
return false;
uint8_t b = 0;
for(uint8_t i=0; i<8; i++)
{
if(current_sample_in_baud == 0) {
if((packet_pos & 7) == 0) { // Load up next byte
current_byte = radio_msg.buffer[packet_pos >> 3];
} else { // Load up next bit
current_byte = current_byte / 2;
}
}
// Toggle tone (1200 <> 2200)
phase_delta = (current_byte & 1) ? PHASE_DELTA_1200 : PHASE_DELTA_2200;
phase += phase_delta; // Add delta-phase (delta-phase tone dependent)
b |= ((phase >> 16) & 1) << i; // Set modulation bit
current_sample_in_baud++;
if(current_sample_in_baud == SAMPLES_PER_BAUD) { // Old bit consumed, load next bit
current_sample_in_baud = 0;
packet_pos++;
}
}
return b;
}
uint8_t ba = HIGH;
uint8_t getFSKbyte(void)
{
uint8_t b = 0;
for(uint8_t i=0; i<8; i++)
{
switch(txs)
{
case 6: // TX-delay
txj++;
if(txj > (uint32_t)(radio_msg.fsk_conf->predelay * radio_msg.fsk_conf->baud / 1000)) {
txj = 0;
txs = 7;
}
break;
case 7: // Transmit a single char
if(txj < radio_msg.bin_len/8) {
txc = radio_msg.buffer[txj]; // Select char
txj++;
ba = LOW; // Start Bit (Synchronizing)
txi = 0;
txs = 8;
} else { // Finished to transmit string
ba = HIGH;
return b;
}
break;
case 8:
if(txi < radio_msg.fsk_conf->bits) {
txi++;
ba = txc & 1;
txc = txc >> 1;
} else {
ba = HIGH; // Stop Bit
txi = 0;
txs = 9;
}
break;
case 9:
if(radio_msg.fsk_conf->stopbits == 2)
ba = HIGH; // Stop Bit
txs = 7;
}
b |= ba << i;
}
return b;
}
static thread_t *feeder_thd = NULL;
static THD_WORKING_AREA(si_fifo_feeder_wa, 1024);
THD_FUNCTION(si_fifo_feeder_thd2, arg)
{
(void)arg;
chRegSetThreadName("radio_tx_feeder");
// Initialize variables for timer
phase_delta = PHASE_DELTA_1200;
phase = 0;
packet_pos = 0;
current_sample_in_baud = 0;
current_byte = 0;
uint8_t localBuffer[129];
uint16_t c = 129;
uint16_t all = (radio_msg.bin_len*SAMPLES_PER_BAUD+7)/8;
// Tune
radioTune(radio_freq, 0, radio_msg.power, 0);
// Initial FIFO fill
for(uint16_t i=0; i<c; i++)
localBuffer[i] = getAFSKbyte();
Si4464_writeFIFO(localBuffer, c);
// Initialize timer
RCC->APB1ENR |= RCC_APB1ENR_TIM7EN;
nvicEnableVector(TIM7_IRQn, 1);
TIM7->ARR = 500;
TIM7->CR1 &= ~STM32_TIM_CR1_ARPE;
TIM7->DIER |= STM32_TIM_DIER_UIE;
// Start transmission
radioTune(radio_freq, 0, radio_msg.power, all);
// Start timer
TIM7->CR1 |= STM32_TIM_CR1_CEN;
while(c < all) { // Do while bytes not written into FIFO completely
// Determine free memory in Si4464-FIFO
uint8_t more = Si4464_freeFIFO();
if(more > all-c) {
if((more = all-c) == 0) // Calculate remainder to send
break; // End if nothing left
}
// Block execution while timer is running
while(TIM7->CR1 & STM32_TIM_CR1_CEN)
chThdSleepMilliseconds(10);
for(uint16_t i=0; i<more; i++)
localBuffer[i] = getAFSKbyte();
Si4464_writeFIFO(localBuffer, more); // Write into FIFO
c += more;
chThdSleepMilliseconds(15);
}
// Shutdown radio (and wait for Si4464 to finish transmission)
shutdownRadio();
chThdExit(MSG_OK);
}
THD_FUNCTION(si_fifo_feeder_thd3, arg)
{
(void)arg;
chRegSetThreadName("radio_tx_feeder");
// Initialize variables for timer
txs = 6;
txc = 0;
txi = 0;
txj = 0;
uint8_t localBuffer[129];
uint16_t c = 129;
uint16_t all = ((radio_msg.fsk_conf->predelay * radio_msg.fsk_conf->baud / 1000) + radio_msg.bin_len + radio_msg.bin_len*(radio_msg.fsk_conf->stopbits+1)/8 + 7) / 8; // FIXME: I transmit more bytes than neccessary
// Initial FIFO fill
for(uint16_t i=0; i<c; i++)
localBuffer[i] = getFSKbyte();
Si4464_writeFIFO(localBuffer, c);
// Start transmission
radioTune(radio_freq, radio_msg.fsk_conf->shift, radio_msg.power, all);
while(c < all) { // Do while bytes not written into FIFO completely
// Determine free memory in Si4464-FIFO
uint8_t more = Si4464_freeFIFO();
if(more > all-c) {
if((more = all-c) == 0) // Calculate remainder to send
break; // End if nothing left
}
for(uint16_t i=0; i<more; i++)
localBuffer[i] = getFSKbyte();
Si4464_writeFIFO(localBuffer, more); // Write into FIFO
c += more;
chThdSleepMilliseconds(500);
}
// Shutdown radio (and wait for Si4464 to finish transmission)
shutdownRadio();
chThdExit(MSG_OK);
}
static void sendAFSK(void) {
// Start/re-start FIFO feeder
feeder_thd = chThdCreateStatic(si_fifo_feeder_wa, sizeof(si_fifo_feeder_wa), HIGHPRIO+1, si_fifo_feeder_thd2, NULL);
// Wait for the transmitter to start (because it is used as mutex)
while(Si4464_getState() != SI4464_STATE_TX)
chThdSleepMilliseconds(1);
}
static void init2GFSK(void) {
@ -84,13 +243,11 @@ static void init2GFSK(void) {
active_mod = MOD_2GFSK;
}
static thread_t *feeder_thd = NULL;
static THD_WORKING_AREA(si_fifo_feeder_wa, 1024);
THD_FUNCTION(si_fifo_feeder_thd, arg)
{
(void)arg;
uint16_t c = 64;
uint16_t c = 129;
uint16_t all = (radio_msg.bin_len+7)/8;
chRegSetThreadName("radio_tx_feeder");
@ -127,98 +284,10 @@ static void send2GFSK(void) {
chThdSleepMilliseconds(1);
}
/**
* Fast interrupt handler for AFSK modulation. It has has the highest priority
* in order to provide an accurate low jitter modulation.
*/
CH_FAST_IRQ_HANDLER(STM32_TIM7_HANDLER)
{
if(active_mod == MOD_AFSK) // AFSK
{
if(packet_pos == radio_msg.bin_len) { // Packet transmission finished
TIM7->CR1 &= ~STM32_TIM_CR1_CEN; // Disable timer
TIM7->SR &= ~STM32_TIM_SR_UIF; // Reset interrupt flag
return;
}
if(current_sample_in_baud == 0) {
if((packet_pos & 7) == 0) { // Load up next byte
current_byte = radio_msg.buffer[packet_pos >> 3];
} else { // Load up next bit
current_byte = current_byte / 2;
}
}
// Toggle tone (1200 <> 2200)
phase_delta = (current_byte & 1) ? PHASE_DELTA_1200 : PHASE_DELTA_2200;
phase += phase_delta; // Add delta-phase (delta-phase tone dependent)
RADIO_WRITE_GPIO((phase >> 16) & 1); // Set modulaton pin (connected to Si4464)
current_sample_in_baud++;
if(current_sample_in_baud == SAMPLES_PER_BAUD) { // Old bit consumed, load next bit
current_sample_in_baud = 0;
packet_pos++;
}
} else { // 2FSK
switch(txs)
{
case 6: // TX-delay
txj++;
if(txj > (uint32_t)(radio_msg.fsk_conf->predelay * radio_msg.fsk_conf->baud / 1000)) {
txj = 0;
txs = 7;
}
break;
case 7: // Transmit a single char
if(txj < radio_msg.bin_len/8) {
txc = radio_msg.buffer[txj]; // Select char
txj++;
RADIO_WRITE_GPIO(LOW); // Start Bit (Synchronizing)
txi = 0;
txs = 8;
} else { // Finished to transmit string
RADIO_WRITE_GPIO(HIGH);
TIM7->CR1 &= ~STM32_TIM_CR1_CEN; // Disable timer
TIM7->SR &= ~STM32_TIM_SR_UIF; // Reset interrupt flag
return;
}
break;
case 8:
if(txi < radio_msg.fsk_conf->bits) {
txi++;
RADIO_WRITE_GPIO(txc & 1);
txc = txc >> 1;
} else {
RADIO_WRITE_GPIO(HIGH); // Stop Bit
txi = 0;
txs = 9;
}
break;
case 9:
if(radio_msg.fsk_conf->stopbits == 2)
RADIO_WRITE_GPIO(HIGH); // Stop Bit
txs = 7;
}
}
palToggleLine(LINE_IO_LED1);
TIM7->SR &= ~STM32_TIM_SR_UIF; // Reset interrupt flag
}
static void initOOK(void) {
// Initialize radio
Si4464_Init();
setModemOOK();
setModemOOK(radio_msg.ook_conf);
active_mod = MOD_OOK;
}
@ -226,58 +295,34 @@ static void initOOK(void) {
* Transmits binary OOK message. One bit = 20ms (1: TONE, 0: NO TONE)
*/
static void sendOOK(void) {
// Tune
radioTune(radio_freq, 0, radio_msg.power, 0);
// Start/re-start FIFO feeder
feeder_thd = chThdCreateStatic(si_fifo_feeder_wa, sizeof(si_fifo_feeder_wa), HIGHPRIO+1, si_fifo_feeder_thd, NULL);
// Transmit data
uint32_t bit = 0;
systime_t time = chVTGetSystemTimeX();
while(bit < radio_msg.bin_len) {
RADIO_WRITE_GPIO((radio_msg.buffer[bit/8] >> (bit%8)) & 0x1);
bit++;
time = chThdSleepUntilWindowed(time, time + MS2ST(1200 / radio_msg.ook_conf->speed));
}
shutdownRadio();
// Wait for the transmitter to start (because it is used as mutex)
while(Si4464_getState() != SI4464_STATE_TX)
chThdSleepMilliseconds(1);
}
static void init2FSK(void) {
// Initialize radio and tune
Si4464_Init();
setModem2FSK();
setModem2FSK(radio_msg.fsk_conf);
active_mod = MOD_2FSK;
}
static void send2FSK(void) {
txs = 6;
txc = 0;
txi = 0;
txj = 0;
// Start/re-start FIFO feeder
feeder_thd = chThdCreateStatic(si_fifo_feeder_wa, sizeof(si_fifo_feeder_wa), HIGHPRIO+1, si_fifo_feeder_thd3, NULL);
// Tune
radioTune(radio_freq, radio_msg.fsk_conf->shift, radio_msg.power, 0);
// Initialize timer
RCC->APB1ENR |= RCC_APB1ENR_TIM7EN;
nvicEnableVector(TIM7_IRQn, 1);
TIM7->ARR = STM32_PCLK1 / 16 / radio_msg.fsk_conf->baud;
TIM7->PSC = 15;
TIM7->CR1 &= ~STM32_TIM_CR1_ARPE;
TIM7->DIER |= STM32_TIM_DIER_UIE;
// Start timer
TIM7->CR1 |= STM32_TIM_CR1_CEN;
// Block execution while timer is running
while(TIM7->CR1 & STM32_TIM_CR1_CEN)
chThdSleepMilliseconds(10);
shutdownRadio();
// Wait for the transmitter to start (because it is used as mutex)
while(Si4464_getState() != SI4464_STATE_TX)
chThdSleepMilliseconds(1);
}
void shutdownRadio(void)
{
// Wait for PH to finish transmission for 2GFSK
while(active_mod == MOD_2GFSK && Si4464_getState() == SI4464_STATE_TX)
// Wait for PH to finish transmission
while(Si4464_getState() == SI4464_STATE_TX)
chThdSleepMilliseconds(10);
Si4464_shutdown();

Wyświetl plik

@ -279,12 +279,36 @@ uint8_t gimage_id; // Global image ID (for all image threads)
mutex_t camera_mtx;
bool camera_mtx_init = false;
/**
* At EOI or if picture is cut prematurely, when buffer has to be flushed
* so all packets are transmitted.
*/
static void flush_ssdv_buffer(prot_t protocol, ax25_t *ax25_handle, radioMSG_t *msg)
{
switch(protocol) {
case PROT_APRS_2GFSK:
case PROT_APRS_AFSK:
if(protocol == PROT_APRS_2GFSK || protocol == PROT_APRS_AFSK)
msg->bin_len = aprs_encode_finalize(ax25_handle);
transmitOnRadio(msg, false);
break;
case PROT_SSDV_2FSK:
transmitOnRadio(msg, false);
msg->bin_len = 0;
break;
default: break;
}
}
void encode_ssdv(const uint8_t *image, uint32_t image_len, module_conf_t* conf, uint8_t image_id, trackPoint_t* captureLocation, bool redudantTx)
{
ssdv_t ssdv;
uint8_t pkt[SSDV_PKT_SIZE];
uint8_t pkt_base91i[150];
uint8_t pkt_base91j[150];
uint8_t pkt_base91i[160];
uint8_t pkt_base91j[160];
const uint8_t *b;
uint32_t bi = 0;
uint8_t c = SSDV_OK;
@ -292,12 +316,12 @@ void encode_ssdv(const uint8_t *image, uint32_t image_len, module_conf_t* conf,
// Init SSDV (FEC at 2FSK, non FEC at APRS)
bi = 0;
ssdv_enc_init(&ssdv, SSDV_TYPE_NORMAL, conf->ssdv_conf.callsign, image_id, conf->ssdv_conf.quality);
ssdv_enc_init(&ssdv, conf->protocol == PROT_SSDV_2FSK ? SSDV_TYPE_NORMAL : SSDV_TYPE_NOFEC, conf->ssdv_conf.callsign, image_id, conf->ssdv_conf.quality);
ssdv_enc_set_buffer(&ssdv, pkt);
// Init transmission packet
radioMSG_t msg;
uint8_t buffer[conf->packet_spacing ? 8192 : 512];
uint8_t buffer[conf->packet_spacing ? 2048 : 8192];
msg.buffer = buffer;
msg.bin_len = 0;
msg.freq = &conf->frequency;
@ -325,12 +349,7 @@ void encode_ssdv(const uint8_t *image, uint32_t image_len, module_conf_t* conf,
if(r <= 0)
{
TRACE_ERROR("SSDV > Premature end of file");
if(conf->protocol == PROT_APRS_2GFSK || conf->protocol == PROT_APRS_AFSK) msg.bin_len = aprs_encode_finalize(&ax25_handle);
if(ax25_handle.size > 0) transmitOnRadio(&msg, false); // Empty buffer
if(conf->protocol == PROT_APRS_2GFSK || conf->protocol == PROT_APRS_AFSK)
{
aprs_encode_init(&ax25_handle, buffer, sizeof(buffer), msg.mod);
}
flush_ssdv_buffer(conf->protocol, &ax25_handle, &msg);
break;
}
ssdv_enc_feed(&ssdv, b, r);
@ -339,21 +358,11 @@ void encode_ssdv(const uint8_t *image, uint32_t image_len, module_conf_t* conf,
if(c == SSDV_EOI)
{
TRACE_INFO("SSDV > ssdv_enc_get_packet said EOI");
if(conf->protocol == PROT_APRS_2GFSK || conf->protocol == PROT_APRS_AFSK) msg.bin_len = aprs_encode_finalize(&ax25_handle);
if(ax25_handle.size > 0) transmitOnRadio(&msg, false); // Empty buffer
if(conf->protocol == PROT_APRS_2GFSK || conf->protocol == PROT_APRS_AFSK)
{
aprs_encode_init(&ax25_handle, buffer, sizeof(buffer), msg.mod);
}
flush_ssdv_buffer(conf->protocol, &ax25_handle, &msg);
break;
} else if(c != SSDV_OK) {
TRACE_ERROR("SSDV > ssdv_enc_get_packet failed: %i", c);
if(conf->protocol == PROT_APRS_2GFSK || conf->protocol == PROT_APRS_AFSK) msg.bin_len = aprs_encode_finalize(&ax25_handle);
if(ax25_handle.size > 0) transmitOnRadio(&msg, false); // Empty buffer
if(conf->protocol == PROT_APRS_2GFSK || conf->protocol == PROT_APRS_AFSK)
{
aprs_encode_init(&ax25_handle, buffer, sizeof(buffer), msg.mod);
}
flush_ssdv_buffer(conf->protocol, &ax25_handle, &msg);
return;
}
@ -364,29 +373,34 @@ void encode_ssdv(const uint8_t *image, uint32_t image_len, module_conf_t* conf,
TRACE_INFO("IMG > Encode APRS/SSDV packet");
// Sync byte, CRC and FEC of SSDV not transmitted (because its not neccessary inside an APRS packet)
pkt[3] = pkt[6];
pkt[4] = pkt[7];
pkt[5] = pkt[8];
base91_encode(&pkt[3 ], pkt_base91i, 110);
pkt[110] = pkt[6];
pkt[111] = pkt[7];
pkt[112] = pkt[8];
base91_encode(&pkt[3+107], pkt_base91j, 110);
base91_encode(&pkt[6 ], pkt_base91i, 109+16);
pkt[112+16] = pkt[6];
pkt[113+16] = pkt[7];
pkt[114+16] = pkt[8];
base91_encode(&pkt[112+16], pkt_base91j, 108+16);
aprs_encode_data_packet(&ax25_handle, 'I', &conf->aprs_conf, pkt_base91i, strlen((char*)pkt_base91i), captureLocation);
aprs_encode_data_packet(&ax25_handle, 'J', &conf->aprs_conf, pkt_base91j, strlen((char*)pkt_base91j), captureLocation);
if(redudantTx)
{
if(conf->protocol == PROT_APRS_AFSK) // AFSK can handle max. 2 packets in the buffer
{
// Transmit packets
flush_ssdv_buffer(conf->protocol, &ax25_handle, &msg);
// Initialize new packet buffer
aprs_encode_init(&ax25_handle, buffer, sizeof(buffer), msg.mod);
}
aprs_encode_data_packet(&ax25_handle, 'I', &conf->aprs_conf, pkt_base91i, strlen((char*)pkt_base91i), captureLocation);
aprs_encode_data_packet(&ax25_handle, 'J', &conf->aprs_conf, pkt_base91j, strlen((char*)pkt_base91j), captureLocation);
}
// Transmit
if(ax25_handle.size >= 58000 || conf->packet_spacing) // Transmit if buffer is almost full or if single packet transmission is activated (packet_spacing != 0)
// Transmit if buffer is almost full or if single packet transmission is activated (packet_spacing != 0)
// or if AFSK is selected (because the encoding takes a lot of buffer)
if(ax25_handle.size >= 58000 || conf->packet_spacing || conf->protocol == PROT_APRS_AFSK)
{
// Transmit packets
msg.bin_len = aprs_encode_finalize(&ax25_handle);
transmitOnRadio(&msg, false);
flush_ssdv_buffer(conf->protocol, &ax25_handle, &msg);
// Initialize new packet buffer
aprs_encode_init(&ax25_handle, buffer, sizeof(buffer), msg.mod);
@ -408,13 +422,9 @@ void encode_ssdv(const uint8_t *image, uint32_t image_len, module_conf_t* conf,
}
// Transmit
if(msg.bin_len >= 61440 || conf->packet_spacing) { // Transmit if buffer is full or if single packet transmission activation (packet_spacing != 0)
// Transmit packets
transmitOnRadio(&msg, false);
if(msg.bin_len >= 32768 || conf->packet_spacing) // Transmit if buffer is full or if single packet transmission activation (packet_spacing != 0)
flush_ssdv_buffer(conf->protocol, NULL, &msg);
// Initialize new packet buffer
msg.bin_len = 0;
}
break;
default:
@ -450,6 +460,9 @@ bool takePicture(ssdv_conf_t *conf, bool enableJpegValidation)
TRACE_INFO("IMG > OV5640 found");
camera_found = true;
// Lock Radio (The radio uses the same DMA for SPI as the camera)
lockRadio(); // Lock radio
// Init camera
if(!camInitialized) {
OV5640_init();
@ -457,9 +470,10 @@ bool takePicture(ssdv_conf_t *conf, bool enableJpegValidation)
}
// Sample data from pseudo DCMI through DMA into RAM
lockRadio(); // Lock radio
conf->size_sampled = OV5640_Snapshot2RAM(conf->ram_buffer, conf->ram_size, conf->res, enableJpegValidation);
unlockRadio(); // Unlock radio
// Unlock radio
unlockRadio();
// Switch off camera
if(!keep_cam_switched_on) {
@ -481,9 +495,13 @@ bool takePicture(ssdv_conf_t *conf, bool enableJpegValidation)
return camera_found;
}
THD_FUNCTION(imgThread, arg) {
THD_FUNCTION(imgThread, arg)
{
module_conf_t* conf = (module_conf_t*)arg;
if(conf->init_delay) chThdSleepMilliseconds(conf->init_delay);
TRACE_INFO("IMG > Startup image thread");
systime_t time = chVTGetSystemTimeX();
while(true)
{
@ -516,10 +534,8 @@ THD_FUNCTION(imgThread, arg) {
void start_image_thread(module_conf_t *conf)
{
if(conf->init_delay) chThdSleepMilliseconds(conf->init_delay);
TRACE_INFO("IMG > Startup image thread");
chsnprintf(conf->name, sizeof(conf->name), "IMG");
thread_t *th = chThdCreateFromHeap(NULL, THD_WORKING_AREA_SIZE(conf->packet_spacing ? 20*1024 : 5*1024), "IMG", NORMALPRIO, imgThread, conf);
thread_t *th = chThdCreateFromHeap(NULL, THD_WORKING_AREA_SIZE(conf->packet_spacing ? 6*1024 : 12*1024), "IMG", NORMALPRIO, imgThread, conf);
if(!th) {
// Print startup error, do not start watchdog for this thread
TRACE_ERROR("IMG > Could not startup thread (not enough memory available)");

Wyświetl plik

@ -135,6 +135,9 @@ THD_FUNCTION(logThread, arg)
{
module_conf_t* conf = (module_conf_t*)arg;
if(conf->init_delay) chThdSleepMilliseconds(conf->init_delay);
TRACE_INFO("LOG > Startup logging thread");
systime_t time = chVTGetSystemTimeX();
while(true)
{
@ -211,8 +214,6 @@ THD_FUNCTION(logThread, arg)
void start_logging_thread(module_conf_t *conf)
{
if(conf->init_delay) chThdSleepMilliseconds(conf->init_delay);
TRACE_INFO("LOG > Startup logging thread");
chsnprintf(conf->name, sizeof(conf->name), "LOG");
thread_t *th = chThdCreateFromHeap(NULL, THD_WORKING_AREA_SIZE(2*1024), "LOG", NORMALPRIO, logThread, conf);
if(!th) {

Wyświetl plik

@ -105,15 +105,25 @@ void replace_placeholders(char* fskmsg, uint16_t size, trackPoint_t *tp) {
str_replace(fskmsg, size, "<LOC>", buf);
}
THD_FUNCTION(posThread, arg) {
THD_FUNCTION(posThread, arg)
{
module_conf_t* conf = (module_conf_t*)arg;
trackPoint_t *trackPoint = getLastTrackPoint();
systime_t time = chVTGetSystemTimeX();
// Wait
if(conf->init_delay) chThdSleepMilliseconds(conf->init_delay);
// Start tracking manager (if not running yet)
init_tracking_manager(true);
// Start position thread
TRACE_INFO("POS > Startup position thread");
// Set telemetry configuration transmission variables
systime_t last_conf_transmission = chVTGetSystemTimeX();
uint32_t current_conf_count = 0;
trackPoint_t *trackPoint;
systime_t time = chVTGetSystemTimeX();
while(true)
{
TRACE_INFO("POS > Do module POSITION cycle");
@ -128,7 +138,7 @@ THD_FUNCTION(posThread, arg) {
TRACE_INFO("POS > Transmit position");
radioMSG_t msg;
uint8_t buffer[256];
uint8_t buffer[512];
msg.buffer = buffer;
msg.freq = &conf->frequency;
msg.power = conf->power;
@ -188,7 +198,7 @@ THD_FUNCTION(posThread, arg) {
memcpy(fskmsg, conf->ukhas_conf.format, sizeof(conf->ukhas_conf.format));
replace_placeholders(fskmsg, sizeof(fskmsg), trackPoint);
str_replace(fskmsg, sizeof(fskmsg), "<CALL>", conf->ukhas_conf.callsign);
msg.bin_len = 8*chsnprintf((char*)msg.buffer, sizeof(fskmsg), "$$$$$%s*%04X\n", fskmsg, crc16(fskmsg));
msg.bin_len = 8*chsnprintf((char*)buffer, sizeof(buffer), "$$$$$%s*%04X\n", fskmsg, crc16(fskmsg));
// Transmit message
transmitOnRadio(&msg, true);
@ -205,7 +215,7 @@ THD_FUNCTION(posThread, arg) {
str_replace(morse, sizeof(morse), "<CALL>", conf->morse_conf.callsign);
// Transmit message
msg.bin_len = morse_encode(msg.buffer, morse); // Convert message to binary stream
msg.bin_len = morse_encode(buffer, sizeof(buffer), morse); // Convert message to binary stream
transmitOnRadio(&msg, true);
break;
@ -220,16 +230,8 @@ THD_FUNCTION(posThread, arg) {
void start_position_thread(module_conf_t *conf)
{
// Wait
if(conf->init_delay) chThdSleepMilliseconds(conf->init_delay);
// Start tracking manager (if not running yet)
init_tracking_manager(true);
// Start position thread
TRACE_INFO("POS > Startup position thread");
chsnprintf(conf->name, sizeof(conf->name), "POS");
thread_t *th = chThdCreateFromHeap(NULL, THD_WORKING_AREA_SIZE(20*1024), "POS", NORMALPRIO, posThread, conf);
thread_t *th = chThdCreateFromHeap(NULL, THD_WORKING_AREA_SIZE(5*1024), "POS", NORMALPRIO, posThread, conf);
if(!th) {
// Print startup error, do not start watchdog for this thread
TRACE_ERROR("POS > Could not startup thread (not enough memory available)");
@ -238,3 +240,4 @@ void start_position_thread(module_conf_t *conf)
conf->wdg_timeout = chVTGetSystemTimeX() + S2ST(1);
}
}

Wyświetl plik

@ -19,6 +19,7 @@ void register_thread_at_wdg(module_conf_t *thread_config)
THD_FUNCTION(wdgThread, arg) {
(void)arg;
uint8_t counter = 0;
while(true)
{
bool healthy = true;
@ -34,8 +35,13 @@ THD_FUNCTION(wdgThread, arg) {
wdgReset(&WDGD1); // Reset hardware watchdog at no error
// Switch LEDs
palToggleLine(LINE_IO_LED2); // Show I'M ALIVE
chThdSleepMilliseconds(healthy ? 500 : 100); // Blink faster (less delay) in case of an error
if(counter++ % (4*healthy) == 0)
{
palClearLine(LINE_IO_LED2);
chThdSleepMilliseconds(50);
palSetLine(LINE_IO_LED2);
}
chThdSleepMilliseconds(1000);
}
}