Add shared waveform processing, generation and firmware image support

This commit is contained in:
2026-09-27 01:45:08 +03:00
parent 2ad29e7ffd
commit cc22c803d1
36 changed files with 2591 additions and 61 deletions

View File

@@ -0,0 +1,88 @@
#include "firmware_image.h"
#include <stdlib.h>
static int hex_digit(char c)
{
if (c >= '0' && c <= '9') return c - '0';
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
return -1;
}
static int space(char c) { return c == ' ' || c == '\t' || c == '\r'; }
static int compare_cells(const void *a, const void *b)
{
uint32_t x = ((const firmware_hex_cell_t *)a)->address;
uint32_t y = ((const firmware_hex_cell_t *)b)->address;
return (x > y) - (x < y);
}
int firmware_hex_parse(const char *text, size_t length,
firmware_hex_cell_t *cells, size_t capacity, size_t max_records,
size_t *count, size_t *error_line)
{
size_t pos = 0, line = 0, used = 0, records = 0, i;
uint32_t upper = 0;
int eof = 0;
if (count == NULL || error_line == NULL) return FW_HEX_FORMAT;
*count = 0; *error_line = 0;
if (text == NULL || cells == NULL) return FW_HEX_FORMAT;
while (pos < length) {
size_t begin = pos, end, n;
uint8_t bytes[260];
unsigned sum = 0, size, kind, address;
while (pos < length && text[pos] != '\n') ++pos;
end = pos;
if (pos < length) ++pos;
++line;
while (begin < end && space(text[begin])) ++begin;
while (end > begin && space(text[end-1])) --end;
if (begin == end) continue;
*error_line = line;
if (eof) return FW_HEX_EOF;
if (++records > max_records) return FW_HEX_CAPACITY;
if (text[begin++] != ':') return FW_HEX_PREFIX;
if ((end-begin) & 1U) return FW_HEX_DIGITS;
n = (end-begin)/2;
if (n < 5 || n > sizeof(bytes)) return FW_HEX_FORMAT;
for (i = 0; i < n; ++i) {
int hi = hex_digit(text[begin+2*i]), lo = hex_digit(text[begin+2*i+1]);
if (hi < 0 || lo < 0) return FW_HEX_DIGITS;
bytes[i] = (uint8_t)(hi*16+lo); sum += bytes[i];
}
size = bytes[0]; address = ((unsigned)bytes[1]<<8) | bytes[2]; kind = bytes[3];
if (n != size+5U) return FW_HEX_LENGTH;
if (sum & 255U) return FW_HEX_CHECKSUM;
if (kind == 0) {
uint64_t absolute = (uint64_t)upper + address;
if (size && absolute + size - 1 > UINT32_MAX) return FW_HEX_RANGE;
if (size > capacity-used) return FW_HEX_CAPACITY;
for (i = 0; i < size; ++i) {
cells[used].address = (uint32_t)(absolute+i);
cells[used++].value = bytes[4+i];
}
} else if (kind == 1) {
if (size || address) return FW_HEX_RECORD;
eof = 1;
} else if (kind == 2 || kind == 4) {
if (size != 2 || address) return FW_HEX_RECORD;
upper = ((uint32_t)bytes[4]<<8) | bytes[5];
upper <<= kind == 2 ? 4 : 16;
} else if (kind == 3 || kind == 5) {
if (size != 4 || address) return FW_HEX_RECORD;
} else return FW_HEX_RECORD;
}
if (!eof || !used) return FW_HEX_EOF;
qsort(cells, used, sizeof(*cells), compare_cells);
for (i = 1; i < used; ++i)
if (cells[i-1].address == cells[i].address) { *error_line = 0; return FW_HEX_OVERLAP; }
*count = used; *error_line = 0;
return FW_HEX_OK;
}
size_t firmware_hex_segment_size(const firmware_hex_cell_t *cells, size_t count, size_t offset)
{
size_t end;
if (cells == NULL || offset >= count) return 0;
end = offset+1;
while (end < count && cells[end-1].address != UINT32_MAX &&
cells[end].address == cells[end-1].address+1U) ++end;
return end-offset;
}

View File

@@ -0,0 +1,115 @@
#include "set_signal.h"
#include <math.h>
#include <stdlib.h>
#include <string.h>
typedef struct { double x, y; } point;
static int compare_point(const void *a, const void *b) {
double x=((const point *)a)->x, y=((const point *)b)->x;
return (x>y)-(x<y);
}
static double edge(double h0,double h1,double d0,double d1) {
double d=((2*h0+h1)*d0-h0*d1)/(h0+h1);
if(d0==0 || (d>0)!=(d0>0)) return 0;
if((d0>0)!=(d1>0) && fabs(d)>3*fabs(d0)) return 3*d0;
return d;
}
static double evaluate(double t,const double *x,const double *y,size_t n,
int method,const double *d,const double *coef,unsigned degree) {
size_t lo=0,hi=n-1,i; double u,a,b,h,value=0;
if(method==SET_SIGNAL_POLYNOMIAL) {
int j; for(j=(int)degree;j>=0;--j) value=value*(2*t-1)+coef[j]; return value;
}
while(hi-lo>1) { i=(hi+lo)/2; if(t<x[i]) hi=i; else lo=i; }
i=lo; h=x[i+1]-x[i]; u=(t-x[i])/h; a=1-u; b=u;
if(method==SET_SIGNAL_LINEAR) return a*y[i]+b*y[i+1];
if(method==SET_SIGNAL_SPLINE)
return a*y[i]+b*y[i+1]+h*h/6*((a*a*a-a)*d[i]+(b*b*b-b)*d[i+1]);
return (2*u*u*u-3*u*u+1)*y[i]+(u*u*u-2*u*u+u)*h*d[i]
+(-2*u*u*u+3*u*u)*y[i+1]+(u*u*u-u*u)*h*d[i+1];
}
int set_signal_reconstruct(const double *input_x,const double *input_y,size_t count,
int method,unsigned degree,size_t output_count,int endpoint,double *out_x,
double *out_y,double *meta,double *work,size_t work_count) {
point *pairs; double *x,*y,*d,*h,*s,*q,*column;
double coef[6]={0},r[6][6]={{0}},origin,span,scale=1,rmse=0;
size_t i,j,k,n=0; unsigned pass;
if(!input_x||!input_y||!out_x||!out_y||!meta||!work || count<2||count>100000
||output_count<2||output_count>10000||work_count<14*count+128
||method<0||method>3||(endpoint!=0&&endpoint!=1)) return 1;
pairs=(point *)work; x=work+2*count; y=x+count; d=y+count; h=d+count;
s=h+count; q=s+count; column=q+6*count;
for(i=0;i<count;++i) {
if(!isfinite(input_x[i])||!isfinite(input_y[i])) return 2;
pairs[i].x=input_x[i]; pairs[i].y=input_y[i];
}
qsort(pairs,count,sizeof(point),compare_point);
for(i=0;i<count;i=j) {
double sum=0; for(j=i+1;j<count&&pairs[j].x==pairs[i].x;++j) {}
for(k=i;k<j;++k) sum+=pairs[k].y/(double)(j-i);
if(!isfinite(sum)) return 2;
x[n]=pairs[i].x; y[n++]=sum; if(fabs(sum)>scale) scale=fabs(sum);
}
if(n<2) return 2;
origin=x[0]; span=x[n-1]-origin;
if(!isfinite(span)||span<=0) return 2;
for(i=0;i<n;++i) { x[i]=(x[i]-origin)/span; y[i]/=scale; d[i]=0; }
for(i=0;i<n-1;++i) {
h[i]=x[i+1]-x[i]; if(h[i]<=0) return 2;
s[i]=(y[i+1]-y[i])/h[i]; if(!isfinite(s[i])) return 2;
}
if(method==SET_SIGNAL_POLYNOMIAL) {
if(degree<1||degree>5||n<=degree) return 1;
for(j=0;j<=degree;++j) {
double norm=0;
for(i=0;i<n;++i) column[i]=pow(2*x[i]-1,(double)j);
for(pass=0;pass<2;++pass) for(k=0;k<j;++k) {
double c=0; for(i=0;i<n;++i) c+=column[i]*q[k*count+i];
r[k][j]+=c; for(i=0;i<n;++i) column[i]-=c*q[k*count+i];
}
for(i=0;i<n;++i) norm+=column[i]*column[i]; norm=sqrt(norm);
if(norm<1e-12*sqrt((double)n)) return 3;
r[j][j]=norm;
for(i=0;i<n;++i) { q[j*count+i]=column[i]/norm; coef[j]+=q[j*count+i]*y[i]; }
}
for(i=degree+1;i>0;--i) {
j=i-1; for(k=j+1;k<=degree;++k) coef[j]-=r[j][k]*coef[k];
coef[j]/=r[j][j];
}
} else if(method==SET_SIGNAL_PCHIP) {
d[0]=d[n-1]=s[0];
if(n>2) {
d[0]=edge(h[0],h[1],s[0],s[1]);
d[n-1]=edge(h[n-2],h[n-3],s[n-2],s[n-3]);
for(i=1;i<n-1;++i) if(s[i-1]!=0&&s[i]!=0&&(s[i-1]>0)==(s[i]>0)) {
double w1=2*h[i]+h[i-1],w2=h[i]+2*h[i-1];
d[i]=(w1+w2)/(w1/s[i-1]+w2/s[i]);
}
}
} else if(method==SET_SIGNAL_SPLINE) {
q[0]=column[0]=0;
for(i=1;i<n-1;++i) {
double diagonal=2*(h[i-1]+h[i])-h[i-1]*q[i-1];
q[i]=h[i]/diagonal;
column[i]=(6*(s[i]-s[i-1])-h[i-1]*column[i-1])/diagonal;
}
for(i=n-2;i>0;--i) d[i]=column[i]-q[i]*d[i+1];
}
for(i=0;i<output_count;++i) {
double t=(double)i/(double)(output_count-(endpoint?1:0));
out_x[i]=origin+span*t; out_y[i]=evaluate(t,x,y,n,method,d,coef,degree)*scale;
if(!isfinite(out_y[i])) return 2;
}
for(i=0;i<count;++i) {
double delta=evaluate((input_x[i]-origin)/span,x,y,n,method,d,coef,degree)-input_y[i]/scale;
rmse+=delta*delta/(double)count;
}
meta[0]=(double)count; meta[1]=(double)n; meta[2]=sqrt(rmse)*scale;
return isfinite(meta[2])?0:2;
}
int set_signal_dac12(const double *volts,size_t count,double vref,uint16_t *codes) {
size_t i;
if(!volts||!codes||count<2||count>10000||!isfinite(vref)||vref<=0) return 1;
for(i=0;i<count;++i) if(!isfinite(volts[i])||volts[i]<0||volts[i]>vref) return 2;
for(i=0;i<count;++i) codes[i]=(uint16_t)floor(volts[i]/vref*4095+0.5);
return 0;
}

View File

@@ -0,0 +1,214 @@
#include "set_wavegen.h"
#include "set_crc.h"
#include <string.h>
static uint16_t read16(const uint8_t *p) {return (uint16_t)((p[0]<<8)|p[1]);}
static void write16(uint8_t *p,unsigned value) {p[0]=(uint8_t)(value>>8);p[1]=(uint8_t)value;}
static size_t seal(uint8_t *p,size_t n) {
uint16_t crc=set_crc16_modbus(p,n);p[n]=(uint8_t)crc;p[n+1]=(uint8_t)(crc>>8);return n+2;
}
void set_wave_init(set_wave_state *s,uint16_t *samples,size_t capacity,const set_wave_port *port) {
memset(s,0,sizeof(*s));s->samples=samples;s->capacity=capacity;s->rate=1000;
if(port) s->port=*port;
}
static unsigned write_register(set_wave_state *s,unsigned reg,unsigned value) {
if(reg==SET_WAVE_BASE+2 && value==0) {
if(s->port.stop) s->port.stop(s->port.context);
s->running=0; return 0;
}
if(s->running) return 6;
if(reg==SET_WAVE_BASE+2 && value==1) {
if(!s->ready||!s->port.start) return 3;
if(s->port.start(s->port.context,s->samples,s->count,s->rate)) return 4;
s->running=1;return 0;
}
if(reg==SET_WAVE_BASE+3) {
uint32_t rate=((uint32_t)s->rate_high<<16)|value;
if(rate<1||rate>SET_WAVE_MAX_RATE) return 3;
s->rate=rate;s->ready=0;return 0;
}
if(reg==SET_WAVE_BASE+8) {
if(value>(SET_WAVE_MAX_RATE>>16)) return 3;
s->rate_high=(uint16_t)value;s->ready=0;return 0;
}
if(reg==SET_WAVE_BASE+4) {
if(value<2||value>SET_WAVE_MAX||value>s->capacity) return 3;
s->count=(uint16_t)value;s->received=s->ready=0;return 0;
}
if(reg>=SET_WAVE_DATA&&reg<SET_WAVE_DATA+SET_WAVE_MAX) {
unsigned index=reg-SET_WAVE_DATA;
if(index>=s->count||index>s->received||value>4095||s->ready) return 3;
if(index<s->received) return s->samples[index]==value?0:3;
s->samples[index]=(uint16_t)value;++s->received;return 0;
}
if(reg==SET_WAVE_BASE+6) {
if(value!=0xA55A||s->count<2||s->received!=s->count) return 3;
s->ready=1;return 0;
}
return 2;
}
unsigned set_wave_read(void *ctx,uint16_t off,uint16_t count,uint16_t *out) {
set_wave_state *s=(set_wave_state *)ctx;uint16_t w[9];unsigned i;
if(!s||!s->samples||!out||!count||(uint32_t)off+count>9)return 2;
w[0]=0x5747;w[1]=3;w[2]=s->running;w[3]=(uint16_t)s->rate;
w[4]=s->count;w[5]=s->received;w[6]=s->ready;w[7]=(uint16_t)s->capacity;w[8]=(uint16_t)(s->rate>>16);
for(i=0;i<count;++i)out[i]=w[off+i];
return 0;
}
unsigned set_wave_write(void *ctx,uint16_t off,uint16_t value) {
set_wave_state *s=(set_wave_state *)ctx;
if(!s||!s->samples||off>=9)return 2;
return write_register(s,SET_WAVE_BASE+off,value);
}
unsigned set_wave_samples_read(void *ctx,uint16_t off,uint16_t count,uint16_t *out) {
set_wave_state *s=(set_wave_state *)ctx;unsigned i;
if(!s||!s->samples||!out||!count||(uint32_t)off+count>s->received)return 2;
for(i=0;i<count;++i)out[i]=s->samples[off+i];
return 0;
}
unsigned set_wave_samples_write(void *ctx,uint16_t off,uint16_t value) {
set_wave_state *s=(set_wave_state *)ctx;
if(!s||!s->samples||off>=SET_WAVE_MAX)return 2;
return write_register(s,SET_WAVE_DATA+off,value);
}
size_t set_wave_rtu(set_wave_state *s,unsigned device,const uint8_t *req,size_t n,uint8_t *out,size_t cap) {
unsigned reg,value,error=0,i;uint16_t status[8];size_t length=0;
if(!s||!s->samples||!req||!out||cap<21||n!=8||device<1||device>247
||req[0]!=device||set_crc16_modbus(req,n)) return 0;
reg=read16(req+2);value=read16(req+4);
if(!((reg>=SET_WAVE_BASE&&reg<SET_WAVE_BASE+9)||(reg>=SET_WAVE_DATA&&reg<SET_WAVE_DATA+SET_WAVE_MAX))) return 0;
out[0]=(uint8_t)device;out[1]=req[1];
if(req[1]==6) {error=write_register(s,reg,value);memcpy(out,req,6);length=6;}
else if(req[1]==3) {
(void)set_wave_read(s,0,8,status);
if(reg==SET_WAVE_BASE&&value==8) {
out[2]=16;for(i=0;i<8;++i) write16(out+3+2*i,status[i]);length=19;
} else if(reg==SET_WAVE_BASE+8&&value==1) {
out[2]=2;write16(out+3,s->rate>>16);length=5;
} else if(reg>=SET_WAVE_DATA&&value==1&&reg-SET_WAVE_DATA<s->received) {
out[2]=2;write16(out+3,s->samples[reg-SET_WAVE_DATA]);length=5;
} else error=2;
} else error=1;
if(error) {out[1]|=0x80;out[2]=(uint8_t)error;length=3;}
return seal(out,length);
}
size_t set_wave_request(unsigned device,unsigned op,unsigned index,unsigned value,uint8_t *out,size_t cap) {
unsigned reg=SET_WAVE_BASE,fn=6;
if(!out||cap<8||device<1||device>247) return 0;
switch(op) {
case 0: fn=3;value=8;break;
case 1: reg+=2;value=0;break;
case 2: reg+=3;if(value>65535)return 0;break;
case 3: reg+=4;if(value<2||value>SET_WAVE_MAX)return 0;break;
case 4: if(index>=SET_WAVE_MAX||value>4095)return 0;reg=SET_WAVE_DATA+index;break;
case 5: reg+=6;value=0xA55A;break;
case 6: reg+=2;value=1;break;
case 7: if(index>=SET_WAVE_MAX)return 0;reg=SET_WAVE_DATA+index;fn=3;value=1;break;
case 8: reg+=8;fn=3;value=1;break;
case 9: reg+=8;if(value>(SET_WAVE_MAX_RATE>>16))return 0;break;
default:return 0;
}
out[0]=(uint8_t)device;out[1]=(uint8_t)fn;write16(out+2,reg);write16(out+4,value);return seal(out,6);
}
int set_wave_response(const uint8_t *req,const uint8_t *reply,size_t size,uint16_t *words,size_t cap) {
size_t expected,i;
if(!req||!reply||!words||set_crc16_modbus(req,8))return -1;
if(size<2)return 0;
if(reply[0]!=req[0])return -1;
if(reply[1]==(req[1]|0x80)) {
if(size<5)return 0;
return size==5&&!set_crc16_modbus(reply,5)?-(100+(int)reply[2]):-1;
}
if(reply[1]!=req[1])return -1;
expected=req[1]==6?8:5+2*(size_t)read16(req+4);
if(size<expected)return 0;
if(size!=expected||set_crc16_modbus(reply,size))return -1;
if(req[1]==6) {if(memcmp(req,reply,8)||cap<1)return -1;words[0]=read16(req+4);return 1;}
if(reply[2]!=expected-5||cap<(expected-5)/2)return -1;
for(i=0;i<(expected-5)/2;++i)words[i]=read16(reply+3+2*i);
if(read16(req+2)==SET_WAVE_BASE&&(words[0]!=0x5747||(words[1]<1||words[1]>3)))return -2;
return (int)((expected-5)/2);
}
size_t set_wave_block_request(unsigned device,unsigned index,const uint16_t *values,
size_t count,uint8_t *out,size_t cap) {
size_t i,n=values?9+2*count:8;
if(!out||device<1||device>247||!count||count>SET_WAVE_BLOCK_MAX||
index>=SET_WAVE_MAX||count>SET_WAVE_MAX-index||cap<n)return 0;
if(values)for(i=0;i<count;++i)if(values[i]>4095)return 0;
out[0]=(uint8_t)device;out[1]=values?16:3;
write16(out+2,SET_WAVE_DATA+index);write16(out+4,(unsigned)count);
if(values) {
out[6]=(uint8_t)(count*2);
for(i=0;i<count;++i)write16(out+7+2*i,values[i]);
}
return seal(out,n-2);
}
size_t set_wave_block_rtu(set_wave_state *s,unsigned device,const uint8_t *req,
size_t n,uint8_t *out,size_t cap) {
unsigned reg,index,count,error=0,i;size_t length=3;
if(!s||!s->samples||!req||!out||cap<5||n<8||device<1||device>247||
req[0]!=device||set_crc16_modbus(req,n))return 0;
reg=read16(req+2);count=read16(req+4);
if(reg<SET_WAVE_DATA||reg>=SET_WAVE_DATA+SET_WAVE_MAX)return 0;
index=reg-SET_WAVE_DATA;out[0]=req[0];out[1]=req[1];
if(!count||count>SET_WAVE_BLOCK_MAX||count>SET_WAVE_MAX-index||
index+count>s->count||index+count>s->capacity)error=3;
else if(req[1]==16) {
if(n!=9+2*(size_t)count||req[6]!=2*count)error=3;
else if(s->running)error=6;
else if(s->ready||index>s->received)error=3;
else {
/* Preflight the whole block, including replayed prefix, before writing. */
for(i=0;i<count;++i) {
unsigned value=read16(req+7+2*i);
if(value>4095||(index+i<s->received&&s->samples[index+i]!=value)) {
error=3;break;
}
}
if(!error) {
if(cap<8)return 0;
for(i=0;i<count;++i)s->samples[index+i]=read16(req+7+2*i);
if(index+count>s->received)s->received=(uint16_t)(index+count);
memcpy(out,req,6);length=6;
}
}
} else if(req[1]==3) {
if(n!=8||index+count>s->received)error=3;
else {
if(cap<5+2*(size_t)count)return 0;
out[2]=(uint8_t)(2*count);
for(i=0;i<count;++i)write16(out+3+2*i,s->samples[index+i]);
length=3+2*(size_t)count;
}
} else error=1;
if(error) {out[1]|=0x80;out[2]=(uint8_t)error;length=3;}
return seal(out,length);
}
int set_wave_block_response(const uint8_t *req,size_t n,const uint8_t *reply,
size_t size,uint16_t *words,size_t cap) {
size_t expected,i;unsigned count;
if(!req||!reply||!words||n<8||set_crc16_modbus(req,n))return -1;
count=read16(req+4);
if(!count||count>SET_WAVE_BLOCK_MAX||read16(req+2)<SET_WAVE_DATA||
(unsigned)read16(req+2)+count>SET_WAVE_DATA+SET_WAVE_MAX)return -1;
if((req[1]==16&&(n!=9+2*(size_t)count||req[6]!=2*count))||
(req[1]==3&&n!=8)||(req[1]!=3&&req[1]!=16))return -1;
if(size<2)return 0;
if(reply[0]!=req[0])return -1;
if(reply[1]==(req[1]|0x80)) {
if(size<5)return 0;
return size==5&&!set_crc16_modbus(reply,size)?-(100+(int)reply[2]):-1;
}
if(reply[1]!=req[1])return -1;
expected=req[1]==16?8:5+2*(size_t)count;
if(size<expected)return 0;
if(size!=expected||set_crc16_modbus(reply,size))return -1;
if(req[1]==16) {
if(cap<1||memcmp(req,reply,6))return -1;
words[0]=(uint16_t)count;return 1;
}
if(cap<count||reply[2]!=2*count)return -1;
for(i=0;i<count;++i)words[i]=read16(reply+3+2*i);
return (int)count;
}