776 lines
34 KiB
C
776 lines
34 KiB
C
|
/* -------------------------------------------------------------------------------------------------- */
|
||
|
/* 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;
|
||
|
}
|