DatvRx/longmynd/main.c
2024-06-04 19:03:30 +02:00

776 lines
34 KiB
C
Executable File

/* -------------------------------------------------------------------------------------------------- */
/* The LongMynd receiver: main.c */
/* - an implementation of the Serit NIM controlling software for the MiniTiouner Hardware */
/* - the top level (main) and command line procesing */
/* Copyright 2019 Heather Lomond */
/* -------------------------------------------------------------------------------------------------- */
/*
This file is part of longmynd.
Longmynd 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.
Longmynd 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 longmynd. If not, see <https://www.gnu.org/licenses/>.
*/
/* -------------------------------------------------------------------------------------------------- */
/* ----------------- INCLUDES ----------------------------------------------------------------------- */
/* -------------------------------------------------------------------------------------------------- */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <pthread.h>
#include "main.h"
#include "ftdi.h"
#include "stv0910.h"
#include "stv0910_regs.h"
#include "stv0910_utils.h"
#include "stv6120.h"
#include "stvvglna.h"
#include "nim.h"
#include "errors.h"
#include "fifo.h"
#include "ftdi_usb.h"
#include "udp.h"
#include "beep.h"
#include "ts.h"
/* -------------------------------------------------------------------------------------------------- */
/* ----------------- DEFINES ------------------------------------------------------------------------ */
/* -------------------------------------------------------------------------------------------------- */
/* Milliseconds between each i2c control loop */
#define I2C_LOOP_MS 100
/* -------------------------------------------------------------------------------------------------- */
/* ----------------- GLOBALS ------------------------------------------------------------------------ */
/* -------------------------------------------------------------------------------------------------- */
static longmynd_config_t longmynd_config = {
.new = false,
.mutex = PTHREAD_MUTEX_INITIALIZER
};
static longmynd_status_t longmynd_status = {
.service_name = "\0",
.service_provider_name = "\0",
.last_updated_monotonic = 0,
.mutex = PTHREAD_MUTEX_INITIALIZER,
.signal = PTHREAD_COND_INITIALIZER
};
static pthread_t thread_ts_parse;
static pthread_t thread_ts;
static pthread_t thread_i2c;
static pthread_t thread_beep;
/* -------------------------------------------------------------------------------------------------- */
/* ----------------- ROUTINES ----------------------------------------------------------------------- */
/* -------------------------------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------------------------------- */
uint64_t timestamp_ms(void) {
/* -------------------------------------------------------------------------------------------------- */
/* Returns the current unix timestamp in milliseconds */
/* return: unix timestamp in milliseconds */
/* -------------------------------------------------------------------------------------------------- */
struct timespec tp;
if(clock_gettime(CLOCK_REALTIME, &tp) != 0)
{
return 0;
}
return (uint64_t) tp.tv_sec * 1000 + tp.tv_nsec / 1000000;
}
void config_set_frequency(uint32_t frequency)
{
if (frequency <= 2450000 && frequency >= 144000)
{
pthread_mutex_lock(&longmynd_config.mutex);
longmynd_config.freq_requested = frequency;
longmynd_config.new = true;
pthread_mutex_unlock(&longmynd_config.mutex);
}
}
void config_set_symbolrate(uint32_t symbolrate)
{
if (symbolrate <= 27500 && symbolrate >= 33)
{
pthread_mutex_lock(&longmynd_config.mutex);
longmynd_config.sr_requested = symbolrate;
longmynd_config.new = true;
pthread_mutex_unlock(&longmynd_config.mutex);
}
}
void config_set_frequency_and_symbolrate(uint32_t frequency, uint32_t symbolrate)
{
if (frequency <= 2450000 && frequency >= 144000
&& symbolrate <= 27500 && symbolrate >= 33)
{
pthread_mutex_lock(&longmynd_config.mutex);
longmynd_config.freq_requested = frequency;
longmynd_config.sr_requested = symbolrate;
longmynd_config.new = true;
pthread_mutex_unlock(&longmynd_config.mutex);
}
}
void config_set_lnbv(bool enabled, bool horizontal)
{
pthread_mutex_lock(&longmynd_config.mutex);
longmynd_config.polarisation_supply = enabled;
longmynd_config.polarisation_horizontal = horizontal;
longmynd_config.new = true;
pthread_mutex_unlock(&longmynd_config.mutex);
}
/* -------------------------------------------------------------------------------------------------- */
uint64_t monotonic_ms(void) {
/* -------------------------------------------------------------------------------------------------- */
/* Returns current value of a monotonic timer in milliseconds */
/* return: monotonic timer in milliseconds */
/* -------------------------------------------------------------------------------------------------- */
struct timespec tp;
if(clock_gettime(CLOCK_MONOTONIC, &tp) != 0)
{
return 0;
}
return (uint64_t) tp.tv_sec * 1000 + tp.tv_nsec / 1000000;
}
/* -------------------------------------------------------------------------------------------------- */
uint8_t process_command_line(int argc, char *argv[], longmynd_config_t *config) {
/* -------------------------------------------------------------------------------------------------- */
/* processes the command line arguments, sets up the parameters in main from them and error checks */
/* All the required parameters are passed in */
/* return: error code */
/* -------------------------------------------------------------------------------------------------- */
uint8_t err=ERROR_NONE;
uint8_t param;
bool main_usb_set=false;
bool ts_ip_set=false;
bool ts_fifo_set=false;
bool status_ip_set=false;
bool status_fifo_set=false;
/* Defaults */
config->port_swap = false;
config->beep_enabled = false;
config->device_usb_addr = 0;
config->device_usb_bus = 0;
config->ts_use_ip = false;
strcpy(config->ts_fifo_path, "longmynd_main_ts");
config->status_use_ip = false;
strcpy(config->status_fifo_path, "longmynd_main_status");
config->polarisation_supply=false;
char polarisation_str[8];
param=1;
while (param<argc-2) {
if (argv[param][0]=='-') {
switch (argv[param++][1]) {
case 'u':
config->device_usb_bus =(uint8_t)strtol(argv[param++],NULL,10);
config->device_usb_addr=(uint8_t)strtol(argv[param ],NULL,10);
main_usb_set=true;
break;
case 'i':
strncpy(config->ts_ip_addr,argv[param++], 16);
config->ts_ip_port=(uint16_t)strtol(argv[param],NULL,10);
config->ts_use_ip=true;
ts_ip_set = true;
break;
case 't':
strncpy(config->status_fifo_path, argv[param], 128);
ts_fifo_set=true;
break;
case 'I':
strncpy(config->status_ip_addr,argv[param++], 16);
config->status_ip_port=(uint16_t)strtol(argv[param],NULL,10);
config->status_use_ip=true;
status_ip_set = true;
break;
case 's':
strncpy(config->status_fifo_path, argv[param], 128);
status_fifo_set=true;
break;
case 'p':
strncpy(polarisation_str, argv[param], 8);
config->polarisation_supply=true;
break;
case 'w':
config->port_swap=true;
param--; /* there is no data for this so go back */
break;
case 'b':
config->beep_enabled=true;
param--; /* there is no data for this so go back */
break;
}
}
param++;
}
if ((argc-param)<2) {
err=ERROR_ARGS_INPUT;
printf("ERROR: Main Frequency and Main Symbol Rate not found.\n");
}
if (err==ERROR_NONE) {
config->freq_requested =(uint32_t)strtol(argv[param++],NULL,10);
if(config->freq_requested==0) {
err=ERROR_ARGS_INPUT;
printf("ERROR: Main Frequency not in a valid format.\n");
}
config->sr_requested =(uint32_t)strtol(argv[param ],NULL,10);
if(config->sr_requested==0) {
err=ERROR_ARGS_INPUT;
printf("ERROR: Main Symbol Rate not in a valid format.\n");
}
}
/* Process LNB Voltage Supply parameter */
if (err==ERROR_NONE && config->polarisation_supply) {
if(0 == strcasecmp("h", polarisation_str)) {
config->polarisation_horizontal=true;
}
else if(0 == strcasecmp("v", polarisation_str)) {
config->polarisation_horizontal=false;
}
else {
config->polarisation_supply = false;
err=ERROR_ARGS_INPUT;
printf("ERROR: Polarisation voltage supply parameter not recognised\n");
}
}
if (err==ERROR_NONE) {
if (config->freq_requested>2450000) {
err=ERROR_ARGS_INPUT;
printf("ERROR: Freq must be <= 2450 MHz\n");
} else if (config->freq_requested<144) {
err=ERROR_ARGS_INPUT;
printf("ERROR: Freq_must be >= 144 MHz\n");
} else if (config->sr_requested>27500) {
err=ERROR_ARGS_INPUT;
printf("ERROR: SR must be <= 27 Msymbols/s\n");
} else if (config->sr_requested<33) {
err=ERROR_ARGS_INPUT;
printf("ERROR: SR must be >= 33 Ksymbols/s\n");
} else if (ts_ip_set && ts_fifo_set) {
err=ERROR_ARGS_INPUT;
printf("ERROR: Cannot set TS FIFO and TS IP address\n");
} else if (status_ip_set && status_fifo_set) {
err=ERROR_ARGS_INPUT;
printf("ERROR: Cannot set Status FIFO and Status IP address\n");
} else if (config->ts_use_ip && config->status_use_ip && (config->ts_ip_port == config->status_ip_port) && (0==strcmp(config->ts_ip_addr, config->status_ip_addr))) {
err=ERROR_ARGS_INPUT;
printf("ERROR: Cannot set Status IP & Port identical to TS IP & Port\n");
} else { /* err==ERROR_NONE */
printf(" Status: Main Frequency=%i KHz\n",config->freq_requested);
printf(" Main Symbol Rate=%i KSymbols/s\n",config->sr_requested);
if (!main_usb_set) printf(" Using First Minitiouner detected on USB\n");
else printf(" USB bus/device=%i,%i\n",config->device_usb_bus,config->device_usb_addr);
if (!config->ts_use_ip) printf(" Main TS output to FIFO=%s\n",config->ts_fifo_path);
else printf(" Main TS output to IP=%s:%i\n",config->ts_ip_addr,config->ts_ip_port);
if (!config->status_use_ip) printf(" Main Status output to FIFO=%s\n",config->status_fifo_path);
else printf(" Main Status output to IP=%s:%i\n",config->status_ip_addr,config->status_ip_port);
if (config->port_swap) printf(" NIM inputs are swapped (Main now refers to BOTTOM F-Type\n");
else printf(" Main refers to TOP F-Type\n");
if (config->beep_enabled) printf(" MER Beep enabled\n");
if (config->polarisation_supply) printf(" Polarisation Voltage Supply enabled: %s\n", (config->polarisation_horizontal ? "H, 18V" : "V, 13V"));
}
}
if (err!=ERROR_NONE) {
printf("Please refer to the longmynd manual page via:\n");
printf(" man -l longmynd.1\n");
}
config->new = true;
return err;
}
/* -------------------------------------------------------------------------------------------------- */
uint8_t do_report(longmynd_status_t *status) {
/* -------------------------------------------------------------------------------------------------- */
/* interrogates the demodulator to find the interesting info to report */
/* status: the state struct */
/* return: error code */
/* -------------------------------------------------------------------------------------------------- */
uint8_t err=ERROR_NONE;
/* LNAs if present */
if (status->lna_ok) {
uint8_t lna_gain, lna_vgo;
if (err==ERROR_NONE) stvvglna_read_agc(NIM_INPUT_TOP, &lna_gain, &lna_vgo);
status->lna_gain = (lna_gain<<5) | lna_vgo;
}
/* I,Q powers */
if (err==ERROR_NONE) err=stv0910_read_power(STV0910_DEMOD_TOP, &status->power_i, &status->power_q);
/* constellations */
if (err==ERROR_NONE) {
for (uint8_t count=0; count<NUM_CONSTELLATIONS; count++) {
stv0910_read_constellation(STV0910_DEMOD_TOP, &status->constellation[count][0], &status->constellation[count][1]);
}
}
/* puncture rate */
if (err==ERROR_NONE) err=stv0910_read_puncture_rate(STV0910_DEMOD_TOP, &status->puncture_rate);
/* carrier frequency offset we are trying */
if (err==ERROR_NONE) err=stv0910_read_car_freq(STV0910_DEMOD_TOP, &status->frequency_offset);
/* symbol rate we are trying */
if (err==ERROR_NONE) err=stv0910_read_sr(STV0910_DEMOD_TOP, &status->symbolrate);
/* viterbi error rate */
if (err==ERROR_NONE) err=stv0910_read_err_rate(STV0910_DEMOD_TOP, &status->viterbi_error_rate);
/* BER */
if (err==ERROR_NONE) err=stv0910_read_ber(STV0910_DEMOD_TOP, &status->bit_error_rate);
/* BCH Uncorrected Flag */
if (err==ERROR_NONE) err=stv0910_read_errors_bch_uncorrected(STV0910_DEMOD_TOP, &status->errors_bch_uncorrected);
/* BCH Error Count */
if (err==ERROR_NONE) err=stv0910_read_errors_bch_count(STV0910_DEMOD_TOP, &status->errors_bch_count);
/* LDPC Error Count */
if (err==ERROR_NONE) err=stv0910_read_errors_ldpc_count(STV0910_DEMOD_TOP, &status->errors_ldpc_count);
/* MER */
if(status->state==STATE_DEMOD_S || status->state==STATE_DEMOD_S2) {
if (err==ERROR_NONE) err=stv0910_read_mer(STV0910_DEMOD_TOP, &status->modulation_error_rate);
} else {
status->modulation_error_rate = 0;
}
/* MODCOD, Short Frames, Pilots */
if (err==ERROR_NONE) err=stv0910_read_modcod_and_type(STV0910_DEMOD_TOP, &status->modcod, &status->short_frame, &status->pilots);
if(status->state!=STATE_DEMOD_S2) {
/* short frames & pilots only valid for S2 DEMOD state */
status->short_frame = 0;
status->pilots = 0;
}
return err;
}
/* -------------------------------------------------------------------------------------------------- */
void *loop_i2c(void *arg) {
/* -------------------------------------------------------------------------------------------------- */
/* Runs a loop to configure and monitor the Minitiouner Receiver */
/* Configuration is read from the configuration struct */
/* Status is written to the status struct */
/* -------------------------------------------------------------------------------------------------- */
thread_vars_t *thread_vars=(thread_vars_t *)arg;
longmynd_status_t *status=(longmynd_status_t *)thread_vars->status;
uint8_t *err = &thread_vars->thread_err;
*err=ERROR_NONE;
longmynd_config_t config_cpy;
longmynd_status_t status_cpy;
uint64_t last_i2c_loop = timestamp_ms();
while (*err==ERROR_NONE && *thread_vars->main_err_ptr==ERROR_NONE) {
/* Receiver State Machine Loop Timer */
do {
/* Sleep for at least 10ms */
usleep(10*1000);
} while (timestamp_ms() < (last_i2c_loop + I2C_LOOP_MS));
/* Check if there's a new config */
if(thread_vars->config->new)
{
/* Lock config struct */
pthread_mutex_lock(&thread_vars->config->mutex);
/* Clone status struct locally */
memcpy(&config_cpy, thread_vars->config, sizeof(longmynd_config_t));
/* Clear new config flag */
thread_vars->config->new = false;
/* Set flag to clear ts buffer */
thread_vars->config->ts_reset = true;
pthread_mutex_unlock(&thread_vars->config->mutex);
status_cpy.frequency_requested = config_cpy.freq_requested;
/* init all the modules */
if (*err==ERROR_NONE) *err=nim_init();
/* we are only using the one demodulator so set the other to 0 to turn it off */
if (*err==ERROR_NONE) *err=stv0910_init(config_cpy.sr_requested,0);
/* we only use one of the tuners in STV6120 so freq for tuner 2=0 to turn it off */
if (*err==ERROR_NONE) *err=stv6120_init(config_cpy.freq_requested,0,config_cpy.port_swap);
/* we turn on the LNA we want and turn the other off (if they exist) */
if (*err==ERROR_NONE) *err=stvvglna_init(NIM_INPUT_TOP, (config_cpy.port_swap) ? STVVGLNA_OFF : STVVGLNA_ON, &status_cpy.lna_ok);
if (*err==ERROR_NONE) *err=stvvglna_init(NIM_INPUT_BOTTOM, (config_cpy.port_swap) ? STVVGLNA_ON : STVVGLNA_OFF, &status_cpy.lna_ok);
if (*err!=ERROR_NONE) printf("ERROR: failed to init a device - is the NIM powered on?\n");
/* Enable/Disable polarisation voltage supply */
if (*err==ERROR_NONE) *err=ftdi_set_polarisation_supply(config_cpy.polarisation_supply, config_cpy.polarisation_horizontal);
if (*err==ERROR_NONE) {
status_cpy.polarisation_supply = config_cpy.polarisation_supply;
status_cpy.polarisation_horizontal = config_cpy.polarisation_horizontal;
}
/* now start the whole thing scanning for the signal */
if (*err==ERROR_NONE) {
*err=stv0910_start_scan(STV0910_DEMOD_TOP);
status_cpy.state=STATE_DEMOD_HUNTING;
}
}
/* Main receiver state machine */
switch(status_cpy.state) {
case STATE_DEMOD_HUNTING:
if (*err==ERROR_NONE) *err=do_report(&status_cpy);
/* process state changes */
if (*err==ERROR_NONE) *err=stv0910_read_scan_state(STV0910_DEMOD_TOP, &status_cpy.demod_state);
if (status_cpy.demod_state==DEMOD_FOUND_HEADER) {
status_cpy.state=STATE_DEMOD_FOUND_HEADER;
}
else if (status_cpy.demod_state==DEMOD_S2) {
status_cpy.state=STATE_DEMOD_S2;
}
else if (status_cpy.demod_state==DEMOD_S) {
status_cpy.state=STATE_DEMOD_S;
}
else if ((status_cpy.demod_state!=DEMOD_HUNTING) && (*err==ERROR_NONE)) {
printf("ERROR: demodulator returned a bad scan state\n");
*err=ERROR_BAD_DEMOD_HUNT_STATE; /* not allowed to have any other states */
} /* no need for another else, all states covered */
break;
case STATE_DEMOD_FOUND_HEADER:
if (*err==ERROR_NONE) *err=do_report(&status_cpy);
/* process state changes */
*err=stv0910_read_scan_state(STV0910_DEMOD_TOP, &status_cpy.demod_state);
if (status_cpy.demod_state==DEMOD_HUNTING) {
status_cpy.state=STATE_DEMOD_HUNTING;
}
else if (status_cpy.demod_state==DEMOD_S2) {
status_cpy.state=STATE_DEMOD_S2;
}
else if (status_cpy.demod_state==DEMOD_S) {
status_cpy.state=STATE_DEMOD_S;
}
else if ((status_cpy.demod_state!=DEMOD_FOUND_HEADER) && (*err==ERROR_NONE)) {
printf("ERROR: demodulator returned a bad scan state\n");
*err=ERROR_BAD_DEMOD_HUNT_STATE; /* not allowed to have any other states */
} /* no need for another else, all states covered */
break;
case STATE_DEMOD_S2:
if (*err==ERROR_NONE) *err=do_report(&status_cpy);
/* process state changes */
*err=stv0910_read_scan_state(STV0910_DEMOD_TOP, &status_cpy.demod_state);
if (status_cpy.demod_state==DEMOD_HUNTING) {
status_cpy.state=STATE_DEMOD_HUNTING;
}
else if (status_cpy.demod_state==DEMOD_FOUND_HEADER) {
status_cpy.state=STATE_DEMOD_FOUND_HEADER;
}
else if (status_cpy.demod_state==DEMOD_S) {
status_cpy.state=STATE_DEMOD_S;
}
else if ((status_cpy.demod_state!=DEMOD_S2) && (*err==ERROR_NONE)) {
printf("ERROR: demodulator returned a bad scan state\n");
*err=ERROR_BAD_DEMOD_HUNT_STATE; /* not allowed to have any other states */
} /* no need for another else, all states covered */
break;
case STATE_DEMOD_S:
if (*err==ERROR_NONE) *err=do_report(&status_cpy);
/* process state changes */
*err=stv0910_read_scan_state(STV0910_DEMOD_TOP, &status_cpy.demod_state);
if (status_cpy.demod_state==DEMOD_HUNTING) {
status_cpy.state=STATE_DEMOD_HUNTING;
}
else if (status_cpy.demod_state==DEMOD_FOUND_HEADER) {
status_cpy.state=STATE_DEMOD_FOUND_HEADER;
}
else if (status_cpy.demod_state==DEMOD_S2) {
status_cpy.state=STATE_DEMOD_S2;
}
else if ((status_cpy.demod_state!=DEMOD_S) && (*err==ERROR_NONE)) {
printf("ERROR: demodulator returned a bad scan state\n");
*err=ERROR_BAD_DEMOD_HUNT_STATE; /* not allowed to have any other states */
} /* no need for another else, all states covered */
break;
default:
*err=ERROR_STATE; /* we should never get here so panic if we do */
break;
}
/* Copy local status data over global object */
pthread_mutex_lock(&status->mutex);
/* Copy out other vars */
status->state = status_cpy.state;
status->demod_state = status_cpy.demod_state;
status->lna_ok = status_cpy.lna_ok;
status->lna_gain = status_cpy.lna_gain;
status->power_i = status_cpy.power_i;
status->power_q = status_cpy.power_q;
status->frequency_requested = status_cpy.frequency_requested;
status->frequency_offset = status_cpy.frequency_offset;
status->polarisation_supply = status_cpy.polarisation_supply;
status->polarisation_horizontal = status_cpy.polarisation_horizontal;
status->symbolrate = status_cpy.symbolrate;
status->viterbi_error_rate = status_cpy.viterbi_error_rate;
status->bit_error_rate = status_cpy.bit_error_rate;
status->modulation_error_rate = status_cpy.modulation_error_rate;
status->errors_bch_uncorrected = status_cpy.errors_bch_uncorrected;
status->errors_bch_count = status_cpy.errors_bch_count;
status->errors_ldpc_count = status_cpy.errors_ldpc_count;
memcpy(status->constellation, status_cpy.constellation, (sizeof(uint8_t) * NUM_CONSTELLATIONS * 2));
status->puncture_rate = status_cpy.puncture_rate;
status->modcod = status_cpy.modcod;
status->short_frame = status_cpy.short_frame;
status->pilots = status_cpy.pilots;
/* Set monotonic value to signal new data */
status->last_updated_monotonic = monotonic_ms();
/* Trigger pthread signal */
pthread_cond_signal(&status->signal);
pthread_mutex_unlock(&status->mutex);
last_i2c_loop = timestamp_ms();
}
return NULL;
}
/* -------------------------------------------------------------------------------------------------- */
uint8_t status_all_write(longmynd_status_t *status, uint8_t (*status_write)(uint8_t, uint32_t), uint8_t (*status_string_write)(uint8_t, char*)) {
/* -------------------------------------------------------------------------------------------------- */
/* Reads the past status struct out to the passed write function */
/* Returns: error code */
/* -------------------------------------------------------------------------------------------------- */
uint8_t err=ERROR_NONE;
/* Main status */
if (err==ERROR_NONE) err=status_write(STATUS_STATE,status->state);
/* LNAs if present */
if (status->lna_ok) {
if (err==ERROR_NONE) err=status_write(STATUS_LNA_GAIN,status->lna_gain);
}
/* I,Q powers */
if (err==ERROR_NONE) err=status_write(STATUS_POWER_I, status->power_i);
if (err==ERROR_NONE) err=status_write(STATUS_POWER_Q, status->power_q);
/* constellations */
for (uint8_t count=0; count<NUM_CONSTELLATIONS; count++) {
if (err==ERROR_NONE) err=status_write(STATUS_CONSTELLATION_I, status->constellation[count][0]);
if (err==ERROR_NONE) err=status_write(STATUS_CONSTELLATION_Q, status->constellation[count][1]);
}
/* puncture rate */
if (err==ERROR_NONE) err=status_write(STATUS_PUNCTURE_RATE, status->puncture_rate);
/* carrier frequency offset we are trying */
/* note we now have the offset, so we need to add in the freq we tried to set it to */
if (err==ERROR_NONE) err=status_write(STATUS_CARRIER_FREQUENCY, (uint32_t)(status->frequency_requested+(status->frequency_offset/1000)));
/* LNB Voltage Supply Enabled: true / false */
if (err==ERROR_NONE) err=status_write(STATUS_LNB_SUPPLY, status->polarisation_supply);
/* LNB Voltage Supply is Horizontal Polarisation: true / false */
if (err==ERROR_NONE) err=status_write(STATUS_LNB_POLARISATION_H, status->polarisation_horizontal);
/* symbol rate we are trying */
if (err==ERROR_NONE) err=status_write(STATUS_SYMBOL_RATE, status->symbolrate);
/* viterbi error rate */
if (err==ERROR_NONE) err=status_write(STATUS_VITERBI_ERROR_RATE, status->viterbi_error_rate);
/* BER */
if (err==ERROR_NONE) err=status_write(STATUS_BER, status->bit_error_rate);
/* MER */
if (err==ERROR_NONE) err=status_write(STATUS_MER, status->modulation_error_rate);
/* BCH Uncorrected Errors Flag */
if (err==ERROR_NONE) err=status_write(STATUS_ERRORS_BCH_UNCORRECTED, status->errors_bch_uncorrected);
/* BCH Corrected Errors Count */
if (err==ERROR_NONE) err=status_write(STATUS_ERRORS_BCH_COUNT, status->errors_bch_count);
/* LDPC Corrected Errors Count */
if (err==ERROR_NONE) err=status_write(STATUS_ERRORS_LDPC_COUNT, status->errors_ldpc_count);
/* Service Name */
if (err==ERROR_NONE) err=status_string_write(STATUS_SERVICE_NAME, status->service_name);
/* Service Provider Name */
if (err==ERROR_NONE) err=status_string_write(STATUS_SERVICE_PROVIDER_NAME, status->service_provider_name);
/* TS Null Percentage */
if (err==ERROR_NONE) err=status_write(STATUS_TS_NULL_PERCENTAGE, status->ts_null_percentage);
/* TS Elementary Stream PIDs */
for (uint8_t count=0; count<NUM_ELEMENT_STREAMS; count++) {
if(status->ts_elementary_streams[count][0] > 0)
{
if (err==ERROR_NONE) err=status_write(STATUS_ES_PID, status->ts_elementary_streams[count][0]);
if (err==ERROR_NONE) err=status_write(STATUS_ES_TYPE, status->ts_elementary_streams[count][1]);
}
}
/* MODCOD */
if (err==ERROR_NONE) err=status_write(STATUS_MODCOD, status->modcod);
/* Short Frames */
if (err==ERROR_NONE) err=status_write(STATUS_SHORT_FRAME, status->short_frame);
/* Pilots */
if (err==ERROR_NONE) err=status_write(STATUS_PILOTS, status->pilots);
return err;
}
/* -------------------------------------------------------------------------------------------------- */
int main(int argc, char *argv[]) {
/* -------------------------------------------------------------------------------------------------- */
/* command line processing */
/* module initialisation */
/* Print out of status information to requested interface, triggered by pthread condition variable */
/* -------------------------------------------------------------------------------------------------- */
uint8_t err;
uint8_t (*status_write)(uint8_t,uint32_t);
uint8_t (*status_string_write)(uint8_t,char*);
printf("Flow: main\n");
err=process_command_line(argc, argv, &longmynd_config);
/* first setup the fifos, udp socket, ftdi and usb */
if(longmynd_config.status_use_ip) {
if (err==ERROR_NONE) err=udp_status_init(longmynd_config.status_ip_addr, longmynd_config.status_ip_port);
status_write = udp_status_write;
status_string_write = udp_status_string_write;
} else {
if (err==ERROR_NONE) err=fifo_status_init(longmynd_config.status_fifo_path);
status_write = fifo_status_write;
status_string_write = fifo_status_string_write;
}
if (err==ERROR_NONE) err=ftdi_init(longmynd_config.device_usb_bus, longmynd_config.device_usb_addr);
thread_vars_t thread_vars_ts = {
.main_err_ptr = &err,
.thread_err = ERROR_NONE,
.config = &longmynd_config,
.status = &longmynd_status
};
if(0 != pthread_create(&thread_ts, NULL, loop_ts, (void *)&thread_vars_ts))
{
fprintf(stderr, "Error creating loop_ts pthread\n");
}
else
{
pthread_setname_np(thread_ts, "TS Transport");
}
thread_vars_t thread_vars_ts_parse = {
.main_err_ptr = &err,
.thread_err = ERROR_NONE,
.config = &longmynd_config,
.status = &longmynd_status
};
if(0 != pthread_create(&thread_ts_parse, NULL, loop_ts_parse, (void *)&thread_vars_ts_parse))
{
fprintf(stderr, "Error creating loop_ts_parse pthread\n");
}
else
{
pthread_setname_np(thread_ts_parse, "TS Parse");
}
thread_vars_t thread_vars_i2c = {
.main_err_ptr = &err,
.thread_err = ERROR_NONE,
.config = &longmynd_config,
.status = &longmynd_status
};
if(0 != pthread_create(&thread_i2c, NULL, loop_i2c, (void *)&thread_vars_i2c))
{
fprintf(stderr, "Error creating loop_i2c pthread\n");
}
else
{
pthread_setname_np(thread_i2c, "Receiver");
}
thread_vars_t thread_vars_beep = {
.main_err_ptr = &err,
.thread_err = ERROR_NONE,
.config = &longmynd_config,
.status = &longmynd_status
};
if(0 != pthread_create(&thread_beep, NULL, loop_beep, (void *)&thread_vars_beep))
{
fprintf(stderr, "Error creating loop_beep pthread\n");
}
else
{
pthread_setname_np(thread_beep, "Beep Audio");
}
uint64_t last_status_sent_monotonic = 0;
longmynd_status_t longmynd_status_cpy;
while (err==ERROR_NONE) {
/* Test if new status data is available */
if(longmynd_status.last_updated_monotonic != last_status_sent_monotonic) {
/* Acquire lock on global status struct */
pthread_mutex_lock(&longmynd_status.mutex);
/* Clone status struct locally */
memcpy(&longmynd_status_cpy, &longmynd_status, sizeof(longmynd_status_t));
/* Release lock on global status struct */
pthread_mutex_unlock(&longmynd_status.mutex);
/* Send all status via configured output interface from local copy */
err=status_all_write(&longmynd_status_cpy, status_write, status_string_write);
/* Update monotonic timestamp last sent */
last_status_sent_monotonic = longmynd_status_cpy.last_updated_monotonic;
} else {
/* Sleep 10ms */
usleep(10*1000);
}
/* Check for errors on threads */
if(err==ERROR_NONE &&
(thread_vars_ts.thread_err!=ERROR_NONE
|| thread_vars_ts_parse.thread_err!=ERROR_NONE
|| thread_vars_beep.thread_err!=ERROR_NONE
|| thread_vars_i2c.thread_err!=ERROR_NONE)) {
err=ERROR_THREAD_ERROR;
}
}
/* Exited, wait for child threads to exit */
pthread_join(thread_ts_parse, NULL);
pthread_join(thread_ts, NULL);
pthread_join(thread_i2c, NULL);
pthread_join(thread_beep, NULL);
return err;
}