/* -------------------------------------------------------------------------------------------------- */ /* 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 . */ /* -------------------------------------------------------------------------------------------------- */ /* ----------------- INCLUDES ----------------------------------------------------------------------- */ /* -------------------------------------------------------------------------------------------------- */ #include #include #include #include #include #include #include #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 (paramdevice_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; countconstellation[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; countconstellation[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; countts_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; }