mirror of
https://github.com/xdsopl/robot36.git
synced 2025-12-06 07:12:07 +01:00
725 lines
24 KiB
Rust
725 lines
24 KiB
Rust
/*
|
|
Copyright 2014 Ahmet Inan <xdsopl@googlemail.com>
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
#pragma version(1)
|
|
#pragma rs java_package_name(xdsopl.robot36)
|
|
|
|
#include "complex.rsh"
|
|
#include "ema.rsh"
|
|
#include "ddc.rsh"
|
|
|
|
short *audio_buffer;
|
|
uchar *value_buffer;
|
|
uchar4 *pixel_buffer;
|
|
|
|
static inline uchar4 rgb(uchar r, uchar g, uchar b) { return (uchar4){ b, g, r, 255 }; }
|
|
|
|
static const float ema_estimator_a = 0.7f;
|
|
static int robot36_scanline_length;
|
|
static float robot36_estimator(int length)
|
|
{
|
|
static ema_t variance = { 0.0f, ema_estimator_a };
|
|
float deviation = robot36_scanline_length - length;
|
|
return filter(&variance, deviation * deviation);
|
|
}
|
|
static int robot72_scanline_length;
|
|
static float robot72_estimator(int length)
|
|
{
|
|
static ema_t variance = { 0.0f, ema_estimator_a };
|
|
float deviation = robot72_scanline_length - length;
|
|
return filter(&variance, deviation * deviation);
|
|
}
|
|
static int martin1_scanline_length;
|
|
static float martin1_estimator(int length)
|
|
{
|
|
static ema_t variance = { 0.0f, ema_estimator_a };
|
|
float deviation = martin1_scanline_length - length;
|
|
return filter(&variance, deviation * deviation);
|
|
}
|
|
static int martin2_scanline_length;
|
|
static float martin2_estimator(int length)
|
|
{
|
|
static ema_t variance = { 0.0f, ema_estimator_a };
|
|
float deviation = martin2_scanline_length - length;
|
|
return filter(&variance, deviation * deviation);
|
|
}
|
|
static int scottie1_scanline_length;
|
|
static float scottie1_estimator(int length)
|
|
{
|
|
static ema_t variance = { 0.0f, ema_estimator_a };
|
|
float deviation = scottie1_scanline_length - length;
|
|
return filter(&variance, deviation * deviation);
|
|
}
|
|
static int scottie2_scanline_length;
|
|
static float scottie2_estimator(int length)
|
|
{
|
|
static ema_t variance = { 0.0f, ema_estimator_a };
|
|
float deviation = scottie2_scanline_length - length;
|
|
return filter(&variance, deviation * deviation);
|
|
}
|
|
static int scottieDX_scanline_length;
|
|
static float scottieDX_estimator(int length)
|
|
{
|
|
static ema_t variance = { 0.0f, ema_estimator_a };
|
|
float deviation = scottieDX_scanline_length - length;
|
|
return filter(&variance, deviation * deviation);
|
|
}
|
|
|
|
static float cnt_fmd_scale;
|
|
static float cnt_fmd(complex_t baseband)
|
|
{
|
|
static complex_t prev;
|
|
float phase = carg(cdiv(baseband, prev));
|
|
prev = baseband;
|
|
return clamp(cnt_fmd_scale * phase, -1.0f, 1.0f);
|
|
}
|
|
|
|
static float dat_fmd_scale;
|
|
static float dat_fmd(complex_t baseband)
|
|
{
|
|
static complex_t prev;
|
|
float phase = carg(cdiv(baseband, prev));
|
|
prev = baseband;
|
|
return clamp(dat_fmd_scale * phase, -1.0f, 1.0f);
|
|
}
|
|
|
|
static ema_t avg_power, leader_lowpass;
|
|
static ddc_t cnt_ddc, dat_ddc;
|
|
static int sample_rate, mode, even_hpos;
|
|
static int maximum_variance, minimum_sync_length;
|
|
static int scanline_length, minimum_length, maximum_length;
|
|
static int vis_timeout, vis_length, bit_length;
|
|
static int break_timeout, break_length;
|
|
static int leader_timeout, leader_length;
|
|
static int first_leader_length, second_leader_length;
|
|
static int buffer_length, bitmap_width, bitmap_height;
|
|
static int sync_length, sync_counter, vpos, hpos;
|
|
static int save_cnt, save_dat, seperator_counter, seperator_length;
|
|
static int u_sep_begin, u_sep_end, v_sep_begin, v_sep_end;
|
|
static int y_begin, y_end, u_begin, u_end, v_begin, v_end;
|
|
static int r_begin, r_end, b_begin, b_end, g_begin, g_end;
|
|
|
|
static const int mode_raw = 0;
|
|
static const int mode_robot36 = 1;
|
|
static const int mode_robot72 = 2;
|
|
static const int mode_martin1 = 3;
|
|
static const int mode_martin2 = 4;
|
|
static const int mode_scottie1 = 5;
|
|
static const int mode_scottie2 = 6;
|
|
static const int mode_scottieDX = 7;
|
|
|
|
static const float robot36_scanline_seconds = 0.15f;
|
|
static const float robot72_scanline_seconds = 0.3f;
|
|
static const float martin1_scanline_seconds = 0.446446f;
|
|
static const float martin2_scanline_seconds = 0.226798f;
|
|
static const float scottie1_scanline_seconds = 0.42822f;
|
|
static const float scottie2_scanline_seconds = 0.277692f;
|
|
static const float scottieDX_scanline_seconds = 1.0503f;
|
|
|
|
void debug_sync()
|
|
{
|
|
save_cnt = 1;
|
|
save_dat = 0;
|
|
mode = mode_raw;
|
|
sync_length = minimum_sync_length;
|
|
maximum_length = buffer_length;
|
|
scanline_length = maximum_length;
|
|
}
|
|
void debug_image()
|
|
{
|
|
save_dat = 1;
|
|
save_cnt = 0;
|
|
mode = mode_raw;
|
|
sync_length = minimum_sync_length;
|
|
maximum_length = buffer_length;
|
|
scanline_length = maximum_length;
|
|
}
|
|
void debug_both()
|
|
{
|
|
save_cnt = 1;
|
|
save_dat = 1;
|
|
mode = mode_raw;
|
|
sync_length = minimum_sync_length;
|
|
maximum_length = buffer_length;
|
|
scanline_length = maximum_length;
|
|
}
|
|
void robot36_mode()
|
|
{
|
|
save_dat = 1;
|
|
save_cnt = 0;
|
|
mode = mode_robot36;
|
|
const float tolerance = 0.8f;
|
|
const float settling_time = 0.0011f;
|
|
const float sync_len = 0.009f;
|
|
const float sync_porch_len = 0.003f;
|
|
const float sep_porch_len = 0.0015f;
|
|
const float y_scan_len = 0.088f;
|
|
const float u_scan_len = 0.044f;
|
|
const float v_scan_len = 0.044f;
|
|
const float seperator_len = 0.0045f;
|
|
seperator_length = seperator_len * sample_rate;
|
|
sync_length = tolerance * sync_len * sample_rate;
|
|
y_begin = (sync_porch_len - settling_time) * sample_rate;
|
|
y_end = y_begin + y_scan_len * sample_rate;
|
|
u_sep_begin = y_end;
|
|
u_sep_end = u_sep_begin + seperator_len * sample_rate;
|
|
u_begin = u_sep_end + sep_porch_len * sample_rate;
|
|
u_end = u_begin + u_scan_len * sample_rate;
|
|
v_sep_begin = u_sep_begin;
|
|
v_sep_end = u_sep_end;
|
|
v_begin = v_sep_end + sep_porch_len * sample_rate;
|
|
v_end = v_begin + v_scan_len * sample_rate;
|
|
scanline_length = robot36_scanline_length;
|
|
maximum_length = scanline_length + sync_porch_len * sample_rate;
|
|
}
|
|
void robot72_mode()
|
|
{
|
|
save_dat = 1;
|
|
save_cnt = 0;
|
|
mode = mode_robot72;
|
|
const float tolerance = 0.8f;
|
|
const float settling_time = 0.0011f;
|
|
const float sync_len = 0.009f;
|
|
const float sync_porch_len = 0.003f;
|
|
const float sep_porch_len = 0.0015f;
|
|
const float y_scan_len = 0.138f;
|
|
const float u_scan_len = 0.069f;
|
|
const float v_scan_len = 0.069f;
|
|
const float seperator_len = 0.0045f;
|
|
seperator_length = seperator_len * sample_rate;
|
|
sync_length = tolerance * sync_len * sample_rate;
|
|
y_begin = (sync_porch_len - settling_time) * sample_rate;
|
|
y_end = y_begin + y_scan_len * sample_rate;
|
|
u_sep_begin = y_end;
|
|
u_sep_end = u_sep_begin + seperator_len * sample_rate;
|
|
u_begin = u_sep_end + sep_porch_len * sample_rate;
|
|
u_end = u_begin + u_scan_len * sample_rate;
|
|
v_sep_begin = u_end;
|
|
v_sep_end = v_sep_begin + seperator_len * sample_rate;
|
|
v_begin = v_sep_end + sep_porch_len * sample_rate;
|
|
v_end = v_begin + v_scan_len * sample_rate;
|
|
scanline_length = robot72_scanline_length;
|
|
maximum_length = scanline_length + sync_porch_len * sample_rate;
|
|
}
|
|
void martin1_mode()
|
|
{
|
|
save_cnt = 0;
|
|
save_dat = 1;
|
|
mode = mode_martin1;
|
|
const float tolerance = 0.5f;
|
|
const float sync_len = 0.004862f;
|
|
const float sync_porch_len = 0.000572f;
|
|
const float r_scan_len = 0.146432f;
|
|
const float g_scan_len = 0.146432f;
|
|
const float b_scan_len = 0.146432f;
|
|
const float seperator_len = 0.000572f;
|
|
seperator_length = seperator_len * sample_rate;
|
|
sync_length = tolerance * sync_len * sample_rate;
|
|
g_begin = 0;
|
|
g_end = g_begin + g_scan_len * sample_rate;
|
|
b_begin = g_end + seperator_len * sample_rate;
|
|
b_end = b_begin + b_scan_len * sample_rate;
|
|
r_begin = b_end + seperator_len * sample_rate;
|
|
r_end = r_begin + r_scan_len * sample_rate;
|
|
scanline_length = martin1_scanline_length;
|
|
maximum_length = scanline_length + sync_porch_len * sample_rate;
|
|
}
|
|
void martin2_mode()
|
|
{
|
|
save_cnt = 0;
|
|
save_dat = 1;
|
|
mode = mode_martin2;
|
|
const float tolerance = 0.5f;
|
|
const float sync_len = 0.004862f;
|
|
const float sync_porch_len = 0.000572f;
|
|
const float r_scan_len = 0.073216f;
|
|
const float g_scan_len = 0.073216f;
|
|
const float b_scan_len = 0.073216f;
|
|
const float seperator_len = 0.000572f;
|
|
seperator_length = seperator_len * sample_rate;
|
|
sync_length = tolerance * sync_len * sample_rate;
|
|
g_begin = 0;
|
|
g_end = g_begin + g_scan_len * sample_rate;
|
|
b_begin = g_end + seperator_len * sample_rate;
|
|
b_end = b_begin + b_scan_len * sample_rate;
|
|
r_begin = b_end + seperator_len * sample_rate;
|
|
r_end = r_begin + r_scan_len * sample_rate;
|
|
scanline_length = martin2_scanline_length;
|
|
maximum_length = scanline_length + sync_porch_len * sample_rate;
|
|
}
|
|
void scottie1_mode()
|
|
{
|
|
save_cnt = 0;
|
|
save_dat = 1;
|
|
mode = mode_scottie1;
|
|
const float tolerance = 0.8f;
|
|
const float settling_time = 0.0011f;
|
|
const float sync_len = 0.009f;
|
|
const float sync_porch_len = 0.0015f;
|
|
const float r_scan_len = 0.138240f;
|
|
const float g_scan_len = 0.138240f;
|
|
const float b_scan_len = 0.138240f;
|
|
const float seperator_len = 0.0015f;
|
|
seperator_length = seperator_len * sample_rate;
|
|
sync_length = tolerance * sync_len * sample_rate;
|
|
r_begin = (sync_porch_len - settling_time) * sample_rate;
|
|
r_end = r_begin + r_scan_len * sample_rate;
|
|
g_begin = r_end + seperator_len * sample_rate;
|
|
g_end = g_begin + g_scan_len * sample_rate;
|
|
b_begin = g_end + seperator_len * sample_rate;
|
|
b_end = b_begin + b_scan_len * sample_rate;
|
|
scanline_length = scottie1_scanline_length;
|
|
maximum_length = scanline_length + sync_porch_len * sample_rate;
|
|
}
|
|
void scottie2_mode()
|
|
{
|
|
save_cnt = 0;
|
|
save_dat = 1;
|
|
mode = mode_scottie2;
|
|
const float tolerance = 0.8f;
|
|
const float settling_time = 0.0011f;
|
|
const float sync_len = 0.009f;
|
|
const float sync_porch_len = 0.0015f;
|
|
const float r_scan_len = 0.088064f;
|
|
const float g_scan_len = 0.088064f;
|
|
const float b_scan_len = 0.088064f;
|
|
const float seperator_len = 0.0015f;
|
|
seperator_length = seperator_len * sample_rate;
|
|
sync_length = tolerance * sync_len * sample_rate;
|
|
r_begin = (sync_porch_len - settling_time) * sample_rate;
|
|
r_end = r_begin + r_scan_len * sample_rate;
|
|
g_begin = r_end + seperator_len * sample_rate;
|
|
g_end = g_begin + g_scan_len * sample_rate;
|
|
b_begin = g_end + seperator_len * sample_rate;
|
|
b_end = b_begin + b_scan_len * sample_rate;
|
|
scanline_length = scottie2_scanline_length;
|
|
maximum_length = scanline_length + sync_porch_len * sample_rate;
|
|
}
|
|
void scottieDX_mode()
|
|
{
|
|
save_cnt = 0;
|
|
save_dat = 1;
|
|
mode = mode_scottieDX;
|
|
const float tolerance = 0.8f;
|
|
const float settling_time = 0.0011f;
|
|
const float sync_len = 0.009f;
|
|
const float sync_porch_len = 0.0015f;
|
|
const float r_scan_len = 0.3456f;
|
|
const float g_scan_len = 0.3456f;
|
|
const float b_scan_len = 0.3456f;
|
|
const float seperator_len = 0.0015f;
|
|
seperator_length = seperator_len * sample_rate;
|
|
sync_length = tolerance * sync_len * sample_rate;
|
|
r_begin = (sync_porch_len - settling_time) * sample_rate;
|
|
r_end = r_begin + r_scan_len * sample_rate;
|
|
g_begin = r_end + seperator_len * sample_rate;
|
|
g_end = g_begin + g_scan_len * sample_rate;
|
|
b_begin = g_end + seperator_len * sample_rate;
|
|
b_end = b_begin + b_scan_len * sample_rate;
|
|
scanline_length = scottieDX_scanline_length;
|
|
maximum_length = scanline_length + sync_porch_len * sample_rate;
|
|
}
|
|
|
|
void initialize(float rate, int length, int width, int height)
|
|
{
|
|
sample_rate = rate;
|
|
buffer_length = length;
|
|
bitmap_width = width;
|
|
bitmap_height = height;
|
|
|
|
vpos = 0;
|
|
even_hpos = hpos = 0;
|
|
even_hpos = 0;
|
|
sync_counter = 0;
|
|
seperator_counter = 0;
|
|
minimum_length = 0.05f * sample_rate;
|
|
minimum_sync_length = 0.002f * sample_rate;
|
|
|
|
robot36_scanline_length = robot36_scanline_seconds * sample_rate;
|
|
robot72_scanline_length = robot72_scanline_seconds * sample_rate;
|
|
martin1_scanline_length = martin1_scanline_seconds * sample_rate;
|
|
martin2_scanline_length = martin2_scanline_seconds * sample_rate;
|
|
scottie1_scanline_length = scottie1_scanline_seconds * sample_rate;
|
|
scottie2_scanline_length = scottie2_scanline_seconds * sample_rate;
|
|
scottieDX_scanline_length = scottieDX_scanline_seconds * sample_rate;
|
|
|
|
const float pairwise_minimum_of_scanline_time_distances = 0.018226f;
|
|
float deviation = pairwise_minimum_of_scanline_time_distances * sample_rate;
|
|
maximum_variance = deviation * deviation;
|
|
|
|
const float first_leader_tolerance = 0.3f;
|
|
const float second_leader_tolerance = 0.9f;
|
|
const float break_tolerance = 0.7f;
|
|
const float leader_timeout_tolerance = 1.1f;
|
|
const float break_timeout_tolerance = 1.8f;
|
|
const float vis_timeout_tolerance = 1.01f;
|
|
const float leader_len = 0.3f;
|
|
const float break_len = 0.01f;
|
|
const float vis_len = 0.3f;
|
|
const float bit_len = 0.03f;
|
|
first_leader_length = first_leader_tolerance * leader_len * sample_rate;
|
|
second_leader_length = second_leader_tolerance * leader_len * sample_rate;
|
|
leader_length = first_leader_length;
|
|
break_length = break_tolerance * break_len * sample_rate;
|
|
vis_length = vis_len * sample_rate;
|
|
bit_length = bit_len * sample_rate;
|
|
leader_timeout = leader_timeout_tolerance * leader_len * sample_rate;
|
|
break_timeout = break_timeout_tolerance * break_len * sample_rate;
|
|
vis_timeout = vis_timeout_tolerance * vis_len * sample_rate;
|
|
|
|
const float dat_carrier = 1900.0f;
|
|
const float cnt_carrier = 1200.0f;
|
|
const float dat_bandwidth = 800.0f;
|
|
const float cnt_bandwidth = 200.0f;
|
|
|
|
avg_power = ema_cutoff(10.0f, sample_rate);
|
|
leader_lowpass = ema_cutoff(100.0f, sample_rate);
|
|
|
|
cnt_ddc = ddc(cnt_carrier, cnt_bandwidth, sample_rate);
|
|
dat_ddc = ddc(dat_carrier, dat_bandwidth, sample_rate);
|
|
|
|
cnt_fmd_scale = sample_rate / (M_PI * cnt_bandwidth);
|
|
dat_fmd_scale = sample_rate / (M_PI * dat_bandwidth);
|
|
|
|
robot36_mode();
|
|
}
|
|
|
|
static void robot36_decoder()
|
|
{
|
|
static prev_timeout;
|
|
if (!prev_timeout && 2 * abs(seperator_counter) > seperator_length)
|
|
vpos = (~1 & vpos) | (seperator_counter > 0);
|
|
prev_timeout = hpos >= maximum_length;
|
|
if (vpos & 1) {
|
|
for (int i = 0; i < bitmap_width; ++i) {
|
|
uchar even_y = value_buffer[i * (y_end-y_begin) / bitmap_width + y_begin];
|
|
uchar u = value_buffer[i * (u_end-u_begin) / bitmap_width + u_begin];
|
|
uchar odd_y = value_buffer[i * (y_end-y_begin) / bitmap_width + even_hpos + y_begin];
|
|
uchar v = value_buffer[i * (v_end-v_begin) / bitmap_width + even_hpos + v_begin];
|
|
pixel_buffer[bitmap_width * (vpos-1) + i] = rsYuvToRGBA_uchar4(even_y, u, v);
|
|
pixel_buffer[bitmap_width * vpos + i] = rsYuvToRGBA_uchar4(odd_y, u, v);
|
|
}
|
|
if (prev_timeout)
|
|
hpos -= scanline_length;
|
|
else
|
|
hpos = 0;
|
|
even_hpos = 0;
|
|
} else {
|
|
if (prev_timeout) {
|
|
even_hpos = scanline_length;
|
|
hpos -= scanline_length;
|
|
} else {
|
|
even_hpos = hpos;
|
|
hpos = 0;
|
|
}
|
|
}
|
|
}
|
|
static void yuv_decoder()
|
|
{
|
|
for (int i = 0; i < bitmap_width; ++i) {
|
|
uchar y = value_buffer[i * (y_end-y_begin) / bitmap_width + y_begin];
|
|
uchar u = value_buffer[i * (u_end-u_begin) / bitmap_width + u_begin];
|
|
uchar v = value_buffer[i * (v_end-v_begin) / bitmap_width + v_begin];
|
|
pixel_buffer[bitmap_width * vpos + i] = rsYuvToRGBA_uchar4(y, u, v);
|
|
}
|
|
if (hpos >= maximum_length)
|
|
hpos -= scanline_length;
|
|
else
|
|
hpos = 0;
|
|
even_hpos = 0;
|
|
}
|
|
static void rgb_decoder()
|
|
{
|
|
for (int i = 0; i < bitmap_width; ++i) {
|
|
uchar r = value_buffer[i * (r_end-r_begin) / bitmap_width + r_begin];
|
|
uchar g = value_buffer[i * (g_end-g_begin) / bitmap_width + g_begin];
|
|
uchar b = value_buffer[i * (b_end-b_begin) / bitmap_width + b_begin];
|
|
pixel_buffer[bitmap_width * vpos + i] = rgb(r, g, b);
|
|
}
|
|
if (hpos >= maximum_length)
|
|
hpos -= scanline_length;
|
|
else
|
|
hpos = 0;
|
|
even_hpos = 0;
|
|
}
|
|
static void raw_decoder()
|
|
{
|
|
for (int i = 0; i < bitmap_width; ++i) {
|
|
uchar value = value_buffer[i * hpos / bitmap_width];
|
|
pixel_buffer[bitmap_width * vpos + i] = rgb(value, value, value);
|
|
}
|
|
even_hpos = hpos = 0;
|
|
}
|
|
|
|
static void reset()
|
|
{
|
|
vpos = 0;
|
|
hpos = 0;
|
|
even_hpos = 0;
|
|
seperator_counter = 0;
|
|
sync_counter = sync_length;
|
|
for (int i = 0; i < bitmap_width * bitmap_height; ++i)
|
|
pixel_buffer[i] = rgb(0, 0, 0);
|
|
}
|
|
|
|
static void scanline_estimator(int sync_level)
|
|
{
|
|
static scanline_counter, sync_counter;
|
|
|
|
int sync_pulse = !sync_level && sync_counter >= minimum_sync_length;
|
|
sync_counter = sync_level ? sync_counter + 1 : 0;
|
|
|
|
if (!sync_pulse) {
|
|
++scanline_counter;
|
|
return;
|
|
}
|
|
|
|
float robot36_var = robot36_estimator(scanline_counter);
|
|
float robot72_var = robot72_estimator(scanline_counter);
|
|
float martin1_var = martin1_estimator(scanline_counter);
|
|
float martin2_var = martin2_estimator(scanline_counter);
|
|
float scottie1_var = scottie1_estimator(scanline_counter);
|
|
float scottie2_var = scottie2_estimator(scanline_counter);
|
|
float scottieDX_var = scottieDX_estimator(scanline_counter);
|
|
scanline_counter = 0;
|
|
|
|
float min_var = min(min(min(robot36_var, robot72_var),
|
|
min(martin1_var, martin2_var)),
|
|
min(min(scottie1_var, scottie2_var), scottieDX_var));
|
|
|
|
int mode_estimated = 0;
|
|
if (min_var > maximum_variance)
|
|
return;
|
|
else if (min_var == robot36_var)
|
|
mode_estimated = mode_robot36;
|
|
else if (min_var == robot72_var)
|
|
mode_estimated = mode_robot72;
|
|
else if (min_var == martin1_var)
|
|
mode_estimated = mode_martin1;
|
|
else if (min_var == martin2_var)
|
|
mode_estimated = mode_martin2;
|
|
else if (min_var == scottie1_var)
|
|
mode_estimated = mode_scottie1;
|
|
else if (min_var == scottie2_var)
|
|
mode_estimated = mode_scottie2;
|
|
else if (min_var == scottieDX_var)
|
|
mode_estimated = mode_scottieDX;
|
|
else
|
|
return;
|
|
|
|
if (mode_estimated == mode)
|
|
return;
|
|
|
|
switch (mode_estimated) {
|
|
case mode_robot36:
|
|
robot36_mode();
|
|
break;
|
|
case mode_robot72:
|
|
robot72_mode();
|
|
break;
|
|
case mode_martin1:
|
|
martin1_mode();
|
|
break;
|
|
case mode_martin2:
|
|
martin2_mode();
|
|
break;
|
|
case mode_scottie1:
|
|
scottie1_mode();
|
|
break;
|
|
case mode_scottie2:
|
|
scottie2_mode();
|
|
break;
|
|
case mode_scottieDX:
|
|
scottieDX_mode();
|
|
break;
|
|
}
|
|
reset();
|
|
}
|
|
|
|
static int calibration_detected(float dat_value, int cnt_active, int cnt_quantized)
|
|
{
|
|
static int progress, countdown;
|
|
static int leader_counter, break_counter;
|
|
|
|
int dat_active = !cnt_active;
|
|
progress = countdown ? progress : 0;
|
|
countdown -= !!countdown;
|
|
|
|
int leader_quantized = round(filter(&leader_lowpass, dat_value));
|
|
int leader_level = dat_active && leader_quantized == 0;
|
|
int leader_pulse = !leader_level && leader_counter >= leader_length;
|
|
leader_counter = leader_level ? leader_counter + 1 : 0;
|
|
if (leader_pulse) {
|
|
if (progress == 2) {
|
|
progress = 3;
|
|
countdown = vis_timeout;
|
|
leader_length = first_leader_length;
|
|
} else {
|
|
progress = 1;
|
|
countdown = break_timeout;
|
|
leader_length = second_leader_length;
|
|
}
|
|
}
|
|
|
|
int break_level = cnt_active && cnt_quantized == 0;
|
|
int break_pulse = !break_level && break_counter >= break_length;
|
|
break_counter = break_level ? break_counter + 1 : 0;
|
|
if (break_pulse) {
|
|
if (progress == 1) {
|
|
progress = 2;
|
|
countdown = leader_timeout;
|
|
} else if (progress < 3) {
|
|
progress = 0;
|
|
leader_length = first_leader_length;
|
|
}
|
|
}
|
|
|
|
if (progress == 3) {
|
|
static int bit_pos, vis_pos, vis_code;
|
|
static int vis_counter, bit_counter;
|
|
if (leader_pulse) {
|
|
bit_pos = 0;
|
|
vis_pos = bit_length;
|
|
bit_counter = 0;
|
|
vis_counter = 0;
|
|
}
|
|
if (++vis_counter < vis_pos) {
|
|
bit_counter += cnt_quantized;
|
|
} else {
|
|
if (bit_pos == 0 && 2 * abs(bit_counter) < bit_length) {
|
|
vis_code = 0;
|
|
} else if (0 < bit_pos && bit_pos < 9 && 2 * abs(bit_counter) > bit_length) {
|
|
int bit_val = bit_counter < 0 ? 1 : 0;
|
|
vis_code |= bit_val << (bit_pos - 1);
|
|
// sometimes stop bit is missing, finish up here.
|
|
if (bit_pos == 8) {
|
|
progress = 0;
|
|
countdown = 0;
|
|
return vis_code;
|
|
}
|
|
} else if (bit_pos == 9 && 2 * abs(bit_counter) < bit_length) {
|
|
progress = 0;
|
|
countdown = 0;
|
|
return vis_code;
|
|
} else {
|
|
progress = 0;
|
|
countdown = 0;
|
|
return -1;
|
|
}
|
|
++bit_pos;
|
|
bit_counter = cnt_quantized;
|
|
vis_pos += bit_length;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static void calibration_detector(float dat_value, int cnt_active, int cnt_quantized)
|
|
{
|
|
switch (calibration_detected(dat_value, cnt_active, cnt_quantized)) {
|
|
case 0x88:
|
|
robot36_mode();
|
|
break;
|
|
case 0x0c:
|
|
robot72_mode();
|
|
break;
|
|
case 0xac:
|
|
martin1_mode();
|
|
break;
|
|
case 0x28:
|
|
martin2_mode();
|
|
break;
|
|
case 0x3c:
|
|
scottie1_mode();
|
|
break;
|
|
case 0xb8:
|
|
scottie2_mode();
|
|
break;
|
|
case 0xcc:
|
|
scottieDX_mode();
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
reset();
|
|
}
|
|
|
|
void decode(int samples) {
|
|
for (int sample = 0; sample < samples; ++sample) {
|
|
float amp = audio_buffer[sample] / 32768.0f;
|
|
float power = amp * amp;
|
|
if (filter(&avg_power, power) < 0.0000001f)
|
|
continue;
|
|
|
|
complex_t cnt_baseband = convert(&cnt_ddc, amp);
|
|
complex_t dat_baseband = convert(&dat_ddc, amp);
|
|
|
|
float cnt_value = cnt_fmd(cnt_baseband);
|
|
float dat_value = dat_fmd(dat_baseband);
|
|
|
|
int cnt_active = cabs(dat_baseband) < cabs(cnt_baseband);
|
|
uchar cnt_level = save_cnt ? 127.5f - 127.5f * cnt_value : 0.0f;
|
|
uchar dat_level = save_dat ? 127.5f + 127.5f * dat_value : 0.0f;
|
|
value_buffer[hpos + even_hpos] = cnt_active ? cnt_level : dat_level;
|
|
|
|
int cnt_quantized = round(cnt_value);
|
|
int dat_quantized = round(dat_value);
|
|
|
|
int sync_level = cnt_active && cnt_quantized == 0;
|
|
int sync_pulse = !sync_level && sync_counter >= sync_length;
|
|
sync_counter = sync_level ? sync_counter + 1 : 0;
|
|
|
|
if (mode != mode_raw) {
|
|
calibration_detector(dat_value, cnt_active, cnt_quantized);
|
|
scanline_estimator(sync_level);
|
|
}
|
|
|
|
int u_sep = u_sep_begin <= hpos && hpos < u_sep_end;
|
|
int v_sep = v_sep_begin <= hpos && hpos < v_sep_end;
|
|
seperator_counter += (u_sep || v_sep) ? dat_quantized : 0;
|
|
|
|
if (++hpos >= maximum_length || sync_pulse) {
|
|
if (hpos < minimum_length) {
|
|
hpos = 0;
|
|
even_hpos = 0;
|
|
seperator_counter = 0;
|
|
continue;
|
|
}
|
|
switch (mode) {
|
|
case mode_robot36:
|
|
robot36_decoder();
|
|
break;
|
|
case mode_robot72:
|
|
yuv_decoder();
|
|
break;
|
|
case mode_martin1:
|
|
case mode_martin2:
|
|
case mode_scottie1:
|
|
case mode_scottie2:
|
|
case mode_scottieDX:
|
|
rgb_decoder();
|
|
break;
|
|
default:
|
|
raw_decoder();
|
|
}
|
|
if (++vpos >= bitmap_height)
|
|
vpos = 0;
|
|
seperator_counter = 0;
|
|
}
|
|
}
|
|
}
|