2010-10-11 40 views
5

Tôi đang làm việc trên một dự án DDS vi điều khiển trong C và có một số vấn đề về cách tính toán nội suy tuyến tính để làm mịn các giá trị đầu ra. Chương trình như bây giờ là
sử dụng 8 bit hàng đầu của bộ tích lũy 24 bit làm chỉ mục cho một mảng giá trị đầu ra 8 bit. Tôi cần phải đưa ra một hàm sẽ lấy byte trung bình và thấp hơn của bộ tích lũy và tạo ra một giá trị ở giữa giá trị "trước" và "tiếp theo" trong mảng. Điều này sẽ đủ đơn giản trên phần cứng nhanh, nhưng kể từ khi tôi đang sử dụng một vi điều khiển tôi thực sự cần phải tránh làm bất kỳ hoạt động điểm nổi hoặc đơn vị nào!Nội suy tuyến tính trong tổng hợp trực tiếp số

Với những hạn chế đó, tôi không chắc chắn về cách lấy giá trị nội suy 8 bit từ hai số đầu vào 8 bit của tôi và 2 byes thấp hơn của bộ tích lũy, đại diện cho "khoảng cách" giữa hai giá trị đầu vào. Cảm ơn trước cho tất cả lời khuyên!

Làm rõ

DDS = Direct Tổng hợp kỹ thuật số

trong DDS một dạng sóng được tạo ra từ một bảng tra cứu sử dụng một giai đoạn ắc. Bộ tích lũy pha thường chứa một thành phần nguyên và một thành phần phân đoạn. Thành phần nguyên được sử dụng như một chỉ mục trong bảng tra cứu. Trong việc triển khai DDS đơn giản, phần phân số bị bỏ qua, nhưng với đầu ra chất lượng cao hơn, thành phần phân đoạn được sử dụng để nội suy (thường là nội suy tuyến tính) giữa các giá trị bảng tra cứu lân cận. Đối với câu hỏi trên, chúng tôi đang xem xét cách thực hiện hiệu quả nội suy tuyến tính này giữa hai giá trị bảng tra cứu cho một phân số đã cho, f, trong đó 0 <= f < 1.

+0

_DDS_ là gì? –

+0

DDS = Tổng hợp kỹ thuật số trực tiếp - được sử dụng để tạo dạng sóng trong âm thanh/radio/thông tin liên lạc/v.v. –

Trả lời

7

Giả sử bạn có một bảng các giá trị dạng sóng (hoặc một góc phần tư hoặc bốn góc phần tư, nó không quan trọng) sau đó một khả năng tối ưu hóa là để lưu trữ giá trị delta betwen giá trị bảng kế tiếp. I E. nếu bạn có ví dụ N = 256 và bảng dạng sóng LUT[N] thì bạn cũng có bảng giá trị delta, LUT_delta[N]. Mối quan hệ giữa hai bảng precomputed là LUT_delta[i] = LUT[i+1] - LUT[i]. Vì vậy, thay vì tìm kiếm hai giá trị bảng liên tiếp, LUT[i]LUT[i+1], trừ các giá trị này để lấy delta, sau đó thực hiện nội suy, bạn chỉ cần tra cứu giá trị bảng đầu tiên, LUT[i] và delta, LUT_delta[i] và sau đó tính giá trị nội suy. Điều này đòi hỏi cùng một số tra cứu bảng, nhưng ít hoạt động toán học hơn. Bạn có thể thực hiện phép nội suy bằng một lệnh tích lũy nhân duy nhất nếu bạn đang sử dụng DSP, nếu không thì đó là thang đo + nhân + thêm vào một CPU mục đích chung.Ngoài ra, nếu bạn xen kẽ các giá trị LUTLUT_delta, bạn có thể tra cứu LUT[i]LUT_delta[i] bằng một lần đọc rồi giải nén hai giá trị.

Pseudo-code:

extract integer LUT index, i, from accumulator // just need a shift for this 
extract fractional part of accumulator, f // mask or subtract to get f 
get p = LUT[i] // lookup waveform value 
get delta = LUT_delta[i] // lookup delta 
calculate p_interp = p + p_delta * f // single multiply-accumulate instruction on most DSPs - need scaling on general purpose CPUs 
+0

Tôi có thể không hiểu bạn một cách chính xác - nhưng với một bộ tích lũy 24 bit sẽ không có nghĩa là tôi phải lưu trữ một giá trị delta cho tất cả 2^16 gia tăng giữa mỗi điểm trong bảng của tôi? Nếu tôi có loại không gian đó, tôi sẽ chỉ lưu trữ bảng ở độ phân giải cao hơn và không làm phiền với nội suy! :) – Bitrex

+0

@Bitrex - không nếu bạn có bảng điểm N (ví dụ: N = 256) thì bạn chỉ cần một bảng điểm N thứ hai cho vùng đồng bằng, trong đó 'LUT_delta [i] = LUT [i + 1] - LUT [i ] '. –

+0

Ah, tôi hiểu rồi. Tôi nghĩ rằng tôi có đủ chỗ cho một cái bàn có kích thước! – Bitrex

-1

Nội suy tuyến tính giữa hai giá trị, ab là (a + b)/2.

Điều này rất dễ dàng trong phần cứng đơn giản và không liên quan đến phân chia hoặc điểm nổi.

Chia-by-2 == dịch chuyển phải một chút.

+0

Điều đó chỉ nội suy điểm giữa - cho câu hỏi trên mà bạn có thể cần phải nội suy một điểm tùy ý. –

+0

"Tùy ý"? Điểm giữa có vẻ như tùy ý như bất kỳ điểm nào khác. –

+1

@ S.Lott: nếu bạn đọc câu hỏi anh ta đang làm DDS với bộ tích lũy pha - phần phân số của bộ tích lũy pha xác định điểm giữa hai giá trị mà bạn cần nội suy. Đối với "tùy ý", từ điển của tôi cho biết: * 2. Toán học (của một hằng số hoặc số lượng khác) giá trị không xác định. * –

1

Nếu bạn muốn chính xác hơn, tôi khuyên bạn nên kiểm tra các bit thấp hơn của bộ tích lũy trước. Ví dụ, nếu chúng ta muốn 4 giá trị đầu ra thay vì 1:

Acc += 0x2000; 
uint lower_bits = Acc & 0xffff; 
int rl = LUT[ Acc >> 16]; 
int rh = LUT[(Acc >> 16) + 1]; 
if (lower_bits < 0x4000) 
    return rl; 
if (lower_bits < 0x8000) 
    return (rl * 3 + rh) >> 2; 
if (lower_bits < 0xC000) 
    return (rl + rh) >> 1; 
return (rl + rh * 3) >> 2; 
4

Để thực hiện nội suy tuyến tính mà không làm chia, bạn nên chắc chắn rằng mẫu số của bạn là một sức mạnh của 2.

giá trị (x) = trước,
giá trị (x + 1) = tiếp theo giá trị (x + dx) = trước đó + (tiếp theo - trước đó) * dx

Câu hỏi của bạn là, làm cách nào để tính toán dx? Bí quyết là phải có chỉ số nội suy của bạn (16 bit thấp của ắc bạn) tính toán sao cho giá trị lớn nhất (dx = 1) là một sức mạnh của hai:

value(x + dx) = previous + ((next - previous) * index)/1024 

Ở đây, bạn đã tính toán giá trị bước của bạn, sao cho bước tối đa là 1024 và corresônd đến dx = 1. Chỉ số = 512 là dành cho dx = 0,5 v.v ...

+0

Tôi thấy, vì vậy trong trường hợp của tôi với một bộ tích lũy 24 bit bằng cách sử dụng 8 bit hàng đầu làm chỉ mục (giá trị 2^16 bước), sự phân chia phải bằng 2^16 hoặc thay đổi đúng 16 bit. – Bitrex

+0

Có, vấn đề là bạn phải có khả năng lưu trữ (tiếp theo - trước đó) * 2^16 trong tính toán của bạn. – shodanex

Các vấn đề liên quan