ThunderScope/Software/libthunderscope/examples/thunderscopehwdump/thunderscopehwdump.c

327 lines
8.4 KiB
C

#include "thunderscopehw.h"
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <string.h>
#ifdef WIN32
#include <windows.h>
#else
#include <unistd.h>
#endif
void write32(uint64_t x, FILE* f) {
uint32_t v = 0;
if (x < (uint64_t)0xFFFFFFFF) v = (uint32_t)x;
if (fwrite(&v, 1, 4, f) != 4) {
perror("fwrite");
exit(1);
}
}
struct Option {
const char* name;
bool needs_argument;
int return_value;
};
struct Option options[] = {
{"device", true, 1 },
{"samples", true, 2 },
{"output-samplerate", true, 3 },
{"help", false, 4 },
{"bw-all", true, 0x10 },
{"bw1", true, 0x11 },
{"bw2", true, 0x12 },
{"bw3", true, 0x13 },
{"bw4", true, 0x14 },
{"vdiv-all", true, 0x20 },
{"vdiv1", true, 0x21 },
{"vdiv2", true, 0x22 },
{"vdiv3", true, 0x23 },
{"vdiv4", true, 0x24 },
{"voffset-all", true, 0x30 },
{"voffset1", true, 0x31 },
{"voffset2", true, 0x32 },
{"voffset3", true, 0x33 },
{"voffset4", true, 0x34 },
{"ac-all", false, 0x40 },
{"ac1", false, 0x41 },
{"ac2", false, 0x42 },
{"ac3", false, 0x43 },
{"ac4", false, 0x44 },
{"dc-all", false, 0x50 },
{"dc1", false, 0x51 },
{"dc2", false, 0x52 },
{"dc3", false, 0x53 },
{"dc4", false, 0x54 },
{"enable-all", false, 0x60 },
{"enable1", false, 0x61 },
{"enable2", false, 0x62 },
{"enable3", false, 0x63 },
{"enable4", false, 0x64 },
};
void usage()
{
fprintf(stderr,
"thunderscopehwdump [options] [filename.wav]\n"
" --device=<deviceid>\n"
" --samples=<number of samples> must be divisable by 4096\n"
" --output-samplerate=<rate> samplerate used in wav file\n"
" --bw[1/2/3/4/-all]=20/100/200/350 (Hz)\n"
" --vdiv[1/2/3/4/-all]=1/2/5/10/20/50/100/200/500/1000/2000/5000/10000 (mV)\n"
" --voffset[1/2/3/4/-all]=<voltage offset> (volts)\n"
" --ac[1/2/3/4/-all]\n"
" --dc[1/2/3/4/-all]\n"
" --enable[1/2/3/4/-all]\n");
}
char* optarg;
int optind = 1;
int mygetopt(int argc, char** argv) {
if (optind >= argc) return -1;
if (!argv[optind][0] == '-' || argv[optind][1] != '-') return -1;
char *arg = strchr(argv[optind], '=');
for (size_t i = 0; i < sizeof(options) / sizeof(options[0]); i++) {
size_t len = strlen(options[i].name);
if (strncmp(options[i].name, argv[optind] + 2, len)) continue;
if (options[i].needs_argument) {
if (!arg) continue;
if (argv[optind] + 2 + len != arg) continue;
optarg = arg + 1;
} else {
if (arg) continue;
if (argv[optind][2 + len]) continue;
optarg = NULL;
}
optind++;
return options[i].return_value;
}
fprintf(stderr, "Unknown option: %s\n", argv[optind]);
usage();
exit(1);
}
int main(int argc, char** argv) {
uint64_t scope_id = 0;
uint64_t samples = 0;
int samplerate = 0;
while (1) {
switch (mygetopt(argc, argv)) {
case 1:
if (!sscanf(optarg, "%" PRIx64, &scope_id)) {
fprintf(stderr, "Scope ID must be hexadecimal.\n");
exit(1);
}
continue;
case 2:
if (!sscanf(optarg, "%" PRId64, &samples)) {
fprintf(stderr, "Number of samples must be a number.\n");
exit(1);
}
continue;
case 3:
if (!sscanf(optarg, "%d", &samplerate)) {
fprintf(stderr, "Output samplerate must be a number.\n");
exit(1);
}
continue;
case 4:
usage();
exit(0);
default:
continue;
case -1:
break;
}
break;
}
if (scope_id == 0) {
uint64_t scope_ids[32];
int scopes = thunderscopehw_scan(scope_ids, 32);
if (scopes == 0) {
fprintf(stderr, "No thunderscopehw hardware found.\n");
exit(1);
}
if (scopes > 1) {
fprintf(stderr, "Multiple scopes found, please select one with --device.\n");
for (int i = 0; i < scopes; i++) {
fprintf(stderr, " %0" PRIx64 "\n", scope_ids[i]);
}
exit(1);
}
scope_id = scope_ids[0];
}
struct ThunderScopeHW *ts = thunderscopehw_create();
enum ThunderScopeHWStatus ret;
ret = thunderscopehw_connect(ts, scope_id);
if (ret != THUNDERSCOPEHW_STATUS_OK) {
fprintf(stderr, "Failed to connecto to thunderscope, error = %s\n", thunderscopehw_describe_error(ret));
exit(1);
}
int enabled_channels = 0;
int num_channels = 0;
optind = 1;
while (1) {
int c = mygetopt(argc, argv);
if (c == -1) break;
if (!(c >> 4)) continue;
for (int channel = 0; channel < 4; channel++) {
if ((c & 0xf) != channel+1 && (c & 0xf) != 0) continue;
switch (c >> 4) {
case 1: // bw
ret = thunderscopehw_bandwidth_set(ts, channel, atoi(optarg));
if (ret != THUNDERSCOPEHW_STATUS_OK) {
fprintf(stderr, "Failed to set bandwidth. error =%s\n", thunderscopehw_describe_error(ret));
exit(1);
}
break;
case 2: // vdiv
ret = thunderscopehw_voltage_division_set(ts, channel, atoi(optarg));
if (ret != THUNDERSCOPEHW_STATUS_OK) {
fprintf(stderr, "Failed to set vdiv. error =%s\n", thunderscopehw_describe_error(ret));
exit(1);
}
break;
case 3: // voffset
ret = thunderscopehw_voltage_offset_set(ts, channel, atof(optarg));
if (ret != THUNDERSCOPEHW_STATUS_OK) {
fprintf(stderr, "Failed to set voffset. error =%s\n", thunderscopehw_describe_error(ret));
exit(1);
}
break;
case 4: // ac
ret = thunderscopehw_ac_couple(ts, channel);
if (ret != THUNDERSCOPEHW_STATUS_OK) {
fprintf(stderr, "Failed to set AC coupling. error =%s\n", thunderscopehw_describe_error(ret));
exit(1);
}
break;
case 5: // dc
ret = thunderscopehw_dc_couple(ts, channel);
if (ret != THUNDERSCOPEHW_STATUS_OK) {
fprintf(stderr, "Failed to set DC coupling. error =%s\n", thunderscopehw_describe_error(ret));
exit(1);
}
break;
}
if ((c >> 4) == 6 || (c & 0xf)) {
ret = thunderscopehw_enable_channel(ts, channel);
if (ret != THUNDERSCOPEHW_STATUS_OK) {
fprintf(stderr, "Failed to enable channel. error =%s\n", thunderscopehw_describe_error(ret));
exit(1);
}
if (!(enabled_channels & (1 << channel))) {
num_channels++;
enabled_channels |= 1 << channel;
}
}
}
}
if (!num_channels) {
fprintf(stderr, "No channels selected.\n");
exit(1);
}
if (samples <= 0) {
fprintf(stderr, "Must select number of samples.\n");
exit(1);
}
if (samples & 4095) {
fprintf(stderr, "Number of samples must be divisible by 4096\n");
exit(1);
}
FILE* outfile = stdout;
if (optind < argc) {
outfile = fopen(argv[optind], "wb");
if (!outfile) {
perror("open output file");
exit(1);
}
}
struct Fmt {
uint16_t pcm;
uint16_t channels;
uint32_t rate;
uint32_t byterate;
uint16_t block_align;
uint16_t bits_per_sample;
};
#ifdef WIN32
Sleep(500);
#else
usleep(500000);
#endif
struct Fmt fmt;
fmt.pcm = 1;
fmt.channels = num_channels;
if (samplerate) {
fmt.rate = samplerate;
} else {
fmt.rate = 1000000000 / fmt.channels;
}
fmt.byterate = 1000000000;
fmt.block_align = 0;
fmt.bits_per_sample = 8;
fwrite("RIFF", 4, 1, outfile);
write32(4ull +
16 + 8 + /* fmt */
samples * fmt.channels + 8 /* data */, outfile);
fwrite("WAVEfmt ", 8, 1, outfile);
write32(16, outfile);
fwrite(&fmt, 16, 1, outfile);
fwrite("data", 4, 1, outfile);
write32(samples * fmt.channels, outfile);
ret = thunderscopehw_start(ts);
if (ret != THUNDERSCOPEHW_STATUS_OK) {
fprintf(stderr, "Failed to start thunderscope, error=%d\n", ret);
exit(1);
}
#define BUFFER_SIZE samples
uint8_t* buffer;
#ifdef _WIN32
buffer = _aligned_malloc(BUFFER_SIZE, 4096);
#else
posix_memalign((void**)&buffer, 4096, BUFFER_SIZE);
#endif
while (samples) {
int64_t to_copy = samples;
if (to_copy > BUFFER_SIZE) to_copy = BUFFER_SIZE;
ret = thunderscopehw_read(ts, buffer, to_copy);
if (ret != THUNDERSCOPEHW_STATUS_OK) {
fprintf(stderr, "Thunderscope read error, error = %s\n", thunderscopehw_describe_error(ret));
exit(1);
}
// Convert signed output to unsigned output
for (size_t i = 0; i < BUFFER_SIZE; i++) {
buffer[i] += 0x80;
}
if (fwrite(buffer, 1, to_copy, outfile) != to_copy) {
perror("fwrite");
exit(1);
}
samples -= to_copy;
}
}