12 Bit ADC waarde omzetten naar LOG bereik

Als ik met mijn counter b.v. tot 50000 wil gaan moet die Char waarde en de "%03d.%02d" worden aangepast.
Zeg dat ik alleen 1 decimaal wil hebben en max. 500000, wordt dit dan: "%06d.%01d" ?

Ho wacht eens even :). De counter is en blijft een integer het is enkel de positie van de rotary encoder.

Pas na berekening met het gegeven bereik en lineair of logaritmisch komt daar pas een frequentie uit en zal je per bereik ook een zinnige resolutie moeten bepalen.

Mensen zijn soms net als een gelijkrichter, ze willen graag hun gelijk hebben.
blackdog

Honourable Member

benleentje en deKees,

Hartelijk dank weer voor de input, langzaamaan valt het kwartje. :)
Morgen met een fris brein het nogmaals goed doorlezen en daarbij ook nadenken welke bereiken ik wil hebben op de genrator.

Voor nu wil ik het niet te uitgebreid dus twee log bereiken en misschien ook nog lineaire bereiken.

Ook nog even nadenken hoe ik het in beeld wil hebben, dit leest niet zo lekker af 49785.1 Hz
Misschien moet dat op de HP manier zoals dit 49 785.1 kHz, dus een spatie na de kHz waarde, 2 123 456.1 MHz, wat is jullie voorkeur?
Ik ben hierover nog niet zeker.

Groet,
Bram

You have your way. I have my way. As for the right way, the correct way, and the only way, it does not exist.

Ik zou kiezen voor 4 (of 5) cijfers met vliegende komma.

Hz tot 1 kHz
"1.000 Hz" / "1.0000 Hz"
"999.9 Hz" / "999.99 Hz

kHz daarboven
"1.000 kHz" of "1.0000 kHz"
"500.0 kHz" of "500.00 kHz"

blackdog

Honourable Member

Hi deKees,

Nog even snel, de AD9833 heeft een resolutie van 0,1HZ, dit omdat de aanwezige clock oscillator 25MHz is.
Dus hij begint bij 0,1Hz aan de onderzijnde en je kan b.v. 1,000.1Hz, of 1,000.000.1MHZ als frequentie instellen zover ik dat goed uit de datasheet begrepen heb.

Uiteindelijk wordt de waarde uit de encoder waarvoor ik max 4x rond neem en dit 2400 pulsen oplevert omgezet door jouw logcode
voor het laagste bereik omgezet in 10.0Hz naar zeg 50kHz, en het hoogste bereik van zeg 600Hz tot 3MHZ.

Eerst maar eens kijken of ik jouw code aan mijn counterwaarde kan knopen en dan de AD9833 aansluiten om "aan te voelen" hoe het werkt.

Groet,
Bram

You have your way. I have my way. As for the right way, the correct way, and the only way, it does not exist.

Op 11 november 2020 23:35:53 schreef blackdog:
wat is jullie voorkeur?

In het originele geval werd de boel ingesteld met stapjes van een deel van een procent. Als je dan zegt dat je gewoon 3 significante cijfers hebt, dan krijg je:

code:


50.1Hz
99.9Hz 
100Hz
234Hz
999Hz
1.00kHz
4.56kHz
10.0kHz
50.0kHz

Misschien wil je 1 decimaal meer als je echt rond de 15 bits als input gebruikt. Met een 600ppr encoder die je een hele slag laat maken heb je iets meer dan 11 bitjes (als je de x4 mode gebruikt). Van 100 naar 101 is dan 1% terwijl de resolutie meer rond de 0.1% ligt. Dus eigenlijk is een decimaal meer wel verantwoord.

Oh. Ik zie dat deKees vrijwel hetzelfde aan het typen is geweest....

@Blackdog: Ja, de DDS chip kan wel 1000000.1 Hz ingesteld krijgen maar als je een logarithmische instelling hebt dan is de vraag of dat relevant is voor jou als gebruiker. Ik zou zoiets eerst "ruw" implementeren. Dan pas later als je het even gebruikt hebt de definitieve keuze maken over instellingen als hoe hard de verandering moet zijn als je aan de knop draait en zo. Zeker als je dingen als minfreq en maxfreq in een constante of variabele duwt kan je die makkelijk aanpassen om te kijken wat jou bevalt.

[Bericht gewijzigd door rew op donderdag 12 november 2020 00:01:55 (25%)

four NANDS do make a NOR . Kijk ook eens in onze shop: http://www.bitwizard.nl/shop/
blackdog

Honourable Member

Hi rew, :)

Als we even het bereik aan houden waar ik mee begon, dat is via een logschaal 10Hz tot 50kHz,
dan is 0,1Hz in de lagere frequenties prettig.
Zeg boven 1kHz is dat niet meer nodig dit omdat je dan al voldoende resolutie hebt voor mijn toepassing.
Maar die 0,1Hz resolutie is gee nprobleem als hij er wel is.

benleentje had al laten zien dat de stap bij de logschaal en rond 50kHz iets van bijna 150Hz was geworden, wat voorlopig goe genog is.

Waar je het verder over hebt betreffende delimiten om deze in variabelen te hangen, dat was ook de bedoeling.
Maar ik wou dit niet allemaal tegelijk hier voorsteleln of laten zien.

Ik wou twee knopjes maken, als eerste voor de laagste frequentie zeg 2kHz en het tweede knopje om de hoogste frequentie in te stellen b.v. 3kHz en dat je dan tussen dat stukje heen en wer kan gaan, met als derde knopje de bevestiging als Lin of Log schaal.

Door veel in variabelen te hangen, krijg ik veel mogelijkheden, maar het moet ook niet te complex worden, het meetinstrumentje moet makkelijk in het gebruik worden zonder veel knoppen en het liefts zonder menu's in het display, dat wil ik dus juist niet. :+

Eerst maar eens de basis logcode aan mijn counter output hangen en kijken of ik dit werkend krijg.

Dank voor je input.

Groet,
Bram

You have your way. I have my way. As for the right way, the correct way, and the only way, it does not exist.

Nog even een stukje code van mijn kant. Ik houd nooit zo van 10000Hz, ik maak daar liever 10kHz van. Dit stukje code doet dat, je geeft aan hoeveel ruimte je in de string wilt reserveren voor je getal. Bijvoorbeeld:

c code:


char buf[50];
ToHumanReadable(buf, sizeof(buf), 11000, int digits = 3);
//output => 11.0k

Hier is de lengte van het getal altijd 3 karakters incl. de punt.

Er zitten nog een paar bugjes in, getest met 3 digits:
- Bij een waarde van 100 wordt de output "100.0", dit moet worden "100". 101 werkt wel goed dat wordt ook echt "101". (Dit herhaald zich voor iedere 10^digits.)
- Als er geen punt wordt weergegeven is de string 1 karakter korter, mooier zou zijn om dan een spatie toe te voegen voor het getal.

Ik wil het nog evenjes niet oplossen, ik heb het idee dat straks de code veteranen komen met: je kan dat beter zo doen. Dus daar kan ik dan mooi van mee genieten. :)

c code:


int ToHumanReadable(char *buf, int size, float value, int digits = 3)
{
	const char smallPrefix[] = "mµnpf";
	const char largePrefix[] = "kMGT";
	float absValue = 0;
	bool isNegative = value < 0;

	if (size < 2)
		return -1; //Not enough size in buffer

	if (isNegative)
		absValue = -value;
	else
		absValue = value;

	float v = log2(absValue) / log2(1000);

	int thousands = (int)v;

	if (v < 0)
		thousands--;

	if (absValue == 0)
		thousands = 0;

	float scaledValue = absValue * pow(1000, -thousands);


	int places = 0;

	if (scaledValue == 0)
		places = digits;
	else
		places = digits - log10(scaledValue);


	int wrt = 0;

	if (isNegative)
		buf[wrt++] = '-';	//Add minus sign.
	
	
	
	

	wrt += snprintf(&buf[wrt], size - wrt, "%.*f", places, scaledValue);


	if (wrt + 2 >= size)
		return -1; //Not enough size in buffer.

	//Enough size for 1 char + \0 char

	if (thousands > 0)
		if (thousands < sizeof(largePrefix))
			buf[wrt++] = largePrefix[thousands - 1];

	if (thousands < 0)
		if (-thousands < sizeof(smallPrefix))
			buf[wrt++] = smallPrefix[-thousands - 1];


	buf[wrt++] = '\0';
	return wrt;
}
PE2BAS

@hardbass: Kwa programmeerstijl zou ik alles met 10^15 vermenigvuldigen. Je "prefixen" zijn dan gewoon "fpnum kMGT". Jou "thousands" bepalen is "tricky": Als 1000-log (1000) niet op 1.000 uitkomt maar in floating point 0.99999 blijkt te zijn... Ik zou herhaald door 1000.0 delen om te bepalen wat de prefix moet zijn. In code ziet dat er langer uit, maar ik betwijfel of het run-time ook langer duurt. De log zal niet goedkoop zijn...

four NANDS do make a NOR . Kijk ook eens in onze shop: http://www.bitwizard.nl/shop/

Niks mis mee, zo op het eerste gezicht.

Natuurlijk zijn er meerdere mogelijkheden om zoiets te doen. Maar dit lijkt ook zeker een effectieve methode.

Ik zou wel de buffer vullen met speciale letters igv 'not enough size'. Dan zie je ook op het schermpje dat er een probleem is. En zo ook bij "size < 2".

Maar deze funktie kan weer wel veel meer dan we hier nodig hebben. Voor deze toepassing is het genoeg om te schakelen tussen "Hz" en "kHz"

IS er een functie die je verteld waar de decimale punt staat in de float? Dan deel je net zolang door 1000 totdat de punt op 3 plaatsen of minder staat

Mensen zijn soms net als een gelijkrichter, ze willen graag hun gelijk hebben.

@benleentje, dat doe ik hier in de for lus.
Daar staat ook een voorwaarde waardoor je nooit buiten de lengte van "prefix" kan komen.

Met de opmerkingen van rew en deKees:

c code:


int ToHumanReadable(char* buf, int size, float value)
{
    const char plusSign = ' ';              //Use \0 to not show this char and not use any space
    const char prefix[] = "fpnum kMGT";     //Use \0 to not show this char and not use any space
    float absValue = 0;
    bool isNegative = value < 0;

    int digits = size - 4;                  //terminator + dot + sign + prefix = 4 characters.

    if (digits <= 0)
    {
        if (size >= 1)
        {
            memset(buf, '-', size);         //Char to display when size is invalid.
            buf[size - 1] = '\0';
        }
        return -1;
    }

    if (isNegative)
        absValue = -value * pow(10, 15);    //Will this be optimized by compiler???
    else
        absValue = value * pow(10, 15);

    int i = 0;

    for(i=0; (i<sizeof(prefix)-2) && (absValue>=1000); i++)
        absValue /= 1000;

    int ditigsBeforeDot = log10(absValue)+1;
    int ditigsAfterDot = digits - ditigsBeforeDot;

    int wrt = 0;

    if (isNegative)
        buf[wrt++] = '-';	            //Add minus sign.
    else if(plusSign != '\0')
        buf[wrt++] = plusSign;              //Add plus sign.

    if (ditigsAfterDot == 0)                //Dont have to show the dot, so add space
        buf[wrt++] = ' ';

    wrt += snprintf(&buf[wrt], size - wrt, "%.*f", ditigsAfterDot, absValue);

    if(prefix[i] != '\0')
        buf[wrt++] = prefix[i];
    buf[wrt++] = '\0';
    return wrt;
}
PE2BAS

@benleentje

code:


  if(f < 1000.)
    ...

[Bericht gewijzigd door deKees op donderdag 12 november 2020 20:44:24 (18%)

Ik zie dat je eeuwig vertrouwen in de preciesheid van floats blijft houden. Mijn gok was dat je leuke "corner cases" zou krijgen en ik schreef het volgende: (Deels jou code volgend. Het plan was om ook de andere kant op te testen, maar omdat ik nu al resultaat had heb ik dat achterwege gelaten). Dat was een bugje: Ik schreef eerst > 1000, waar >= 1000 moest staan.

c code:


#include <math.h>
#include <stdio.h>
#include <stdlib.h>

void testit (float t)
{
  int pi;

   printf ("number= %f, ", t);
   t = t * pow (10, 15);
 
   for (pi =0 ; t >= 1000;pi++)
       t = t / 1000;
   printf ("pi = %d, remain=%12f, numdigits= %f\n", 
      pi, t, log10 (t) + 1 );
}

int main (int argc, char **argv)
{
  float t;
  int i, j;

  for (i = 0 ; i < 10;i++) {
     t = 1; 
     for (j=0;j<i;j++) t = t * 10;
     testit (t);
  }
  for (i = 0 ; i < 16;i++) {
     t = 1; 
     for (j=0;j<i;j++) t = t / 10;
     testit (t);
  }
}

Wat je ziet is dat bij de groter-dan-1 gevallen alles steeds precies goed gaat. Maar bij de <1 getallen gaat het wel eens fout:

code:


number= 1.000000, pi = 5, remain=    1.000000, numdigits= 1.000000
number= 10.000000, pi = 5, remain=   10.000000, numdigits= 2.000000
number= 100.000000, pi = 5, remain=  100.000000, numdigits= 3.000000
number= 1000.000000, pi = 6, remain=    1.000000, numdigits= 1.000000
number= 10000.000000, pi = 6, remain=   10.000000, numdigits= 2.000000
number= 100000.000000, pi = 6, remain=  100.000000, numdigits= 3.000000
number= 1000000.000000, pi = 7, remain=    1.000000, numdigits= 1.000000
number= 10000000.000000, pi = 7, remain=   10.000000, numdigits= 2.000000
number= 100000000.000000, pi = 7, remain=  100.000000, numdigits= 3.000000
number= 1000000000.000000, pi = 8, remain=    1.000000, numdigits= 1.000000
number= 1.000000, pi = 5, remain=    1.000000, numdigits= 1.000000
number= 0.100000, pi = 4, remain=  100.000000, numdigits= 3.000000
number= 0.010000, pi = 4, remain=   10.000000, numdigits= 2.000000
number= 0.001000, pi = 3, remain=  999.999939, numdigits= 4.000000
number= 0.000100, pi = 3, remain=   99.999992, numdigits= 3.000000
number= 0.000010, pi = 3, remain=    9.999999, numdigits= 2.000000
number= 0.000001, pi = 2, remain=  999.999878, numdigits= 4.000000
number= 0.000000, pi = 2, remain=   99.999985, numdigits= 3.000000
number= 0.000000, pi = 2, remain=    9.999999, numdigits= 2.000000
number= 0.000000, pi = 1, remain=  999.999878, numdigits= 4.000000
number= 0.000000, pi = 1, remain=   99.999985, numdigits= 3.000000
number= 0.000000, pi = 1, remain=    9.999999, numdigits= 2.000000
number= 0.000000, pi = 0, remain=  999.999878, numdigits= 4.000000
number= 0.000000, pi = 0, remain=   99.999992, numdigits= 3.000000
number= 0.000000, pi = 0, remain=    9.999999, numdigits= 2.000000
number= 0.000000, pi = 0, remain=    1.000000, numdigits= 1.000000

De 1-duizendste is de eerste. de 0.1 en 0.01 willen met pi =4 dus 100m<eenheid> gaan printen. Maar de 1/1000 wordt dan dus 1000u, waarbij het "remain" ding duidelijk 999 is maar numdigits dus op 4 uitkomt.

De manier om dit goed te doen is om alles met integers te doen zodra dat mogelijk is. Wil je vier significante digits printen dan wil je dus een getal krijgen tussen de 999.5 en de 9999.50 . Zoek je schaalfactor op, vermenigvuldig en rond af op hele cijfers. Zo! Nu heb je een integer. Die kan je converteren, een punt tussen schoffelen en een eenheid-schaal-factor achter plakken.

Tot mijn verbazing lijkt de log10 functie nauwkeuriger te zijn dan ik had gedacht: voor vrijwel alle machten van tien binnen het bereik van float en double komen er exacte getallen uit.

Misschien dat meespeelt dat de intel CPU waarop ik dit draai iets van 16 extra bitjes in de FPU meeneemt... Hmm. Arm FPU geeft precies hetzelfde resultaat. Nouja. Mijn eerste code toont aan dat je toch wel eens onverwachte resultaten kan krijgen zelfs al is je log10 functie precies goed bij alle machten van tien.

four NANDS do make a NOR . Kijk ook eens in onze shop: http://www.bitwizard.nl/shop/

Mooie uitgebreide test. Je ziet duidelijk dat de floats niet meer precies genoeg zijn. Bijzondere afwijking tussen remain en numdigits. De vraag is ook nog wat de compiler onderwater doet. Ik weet niet precies hoe die log10 functie eruit ziet. Wiskundig mag je LOGn(x) = LOGa(X) / LOGa(n) doen. Misschien wel leuk om het eens te bouwen met integers. Al is het maar ter leering ende vermaeck.

Het is erg makkelijk om met floats en doubles te werken, maar dit zijn zo van die dingen waar je wel erg in moet hebben. Ik heb wel eens een idee gehad om zelf getallen op te slaan als delingen. Dus A/B, maar dat is bij een idee gebleven, ik weet niet of dat realistisch is om zelf te maken. In C++ kun je wel de operator overloads gebruiken dat zou het wellicht iets gebruiksvriendelijker maken.

Wat ook goed werkt, als je weet hoe nauwkeurig je wilt werken, is alles in integers doen die een factor groter zijn. Dus in plaats dat je in je variabele volts opslaat heb je het over millivolt.

PE2BAS

Ja, in het onderhavige geval weten we bijvoorbeeld dat de laagste frequentie 10Hz is. Als je dan in mHz als eenheid gaat werkten dan heb je vier cijfers resolutie bij 10Hz, en een 32-bit integer is genoeg om voorbij 50kHz te komen. Maar let op: Bij 31 bits houdt het bij 2MHz op en bij 32 bits bij 4MHz. Voor minder dan een tientje kan je DDS chips kopen die hier voorbij gaan....

four NANDS do make a NOR . Kijk ook eens in onze shop: http://www.bitwizard.nl/shop/
blackdog

Honourable Member

Hi,

Heel vele input waar ik de ballen niet van begrijp. ;)

Laat ik deze week eerst een begin setup maken met de variabelen en wat andere zaken zoals de bereiken die ik zou willen hebben.
Zodat ik stap voor stap verder kan gaan zodat mijn bein de code kan volgen.

Naturulijk begrijp ik wel dat jullie hier de deviatie/nauwkeurigheid bespreken van de log conversie, ook dat je beter met hele getallen kan werken die je daarna door deling voorziet van eenheden achter de komma.

Voor mij is een perfecte log conversie niet echt nodig, als je kijkt naar het voorbeeldje dat benleentje liet zien van de uitkomts van de code die door deKees is gemaakt dan zie je in het berijk tot 50khz dat aan het einde de stapjes iets van 150Hz zijn.
En of die stapjes daar nu 149.325 of 148,851Hz is boeid mij niet in dit meetinstrument.

Maar zoals altijd, het is wel van belang dat je op de hoogte bent waar de beperkingen zitten in waar je mee bezig bent, dan kan je daar nu of later rekening mee houden.
Dus het is in het geheel geen topic kaping zoals hardbass opmerkte. :)

Dus als het werk het toelaat, en ik kan vinden hoe ik de tweede SPI bus op de Teensy aan het werk krijg voor de AD9833, dan zal ik hier mijn code posten.
Oja twee SPI bussen, mijn display heeft geen chip select, dus dat is een eigenheimer op de SPI bus waarop hij zit aangesloten.

Groet,
Bram

You have your way. I have my way. As for the right way, the correct way, and the only way, it does not exist.

Wel wel,
Inderdaad, floats werken binair, en een getal als 0.1 resulteert in binaire notatie in een oneindig repeterende breuk, en kan dus in principe niet exact worden vastgelegd.

Maar het gaat hier over de range 10 tot 50000, dus dit probleem speelt hier helemaal niet. En dan nog zijn er wel simpeler oplossingen dan alles weer met integers te gaan doen. Ik kreeg op school (in de middeleeuwen) al te horen dat je bij floats altijd moet rekenen met een kleine marge.

blackdog

Honourable Member

deKees,

School, wat is dat?
Ik kan mij iets herrinneren diep in de vorige eeuw, was niet verder gekomen dan de LTS... tja en bijna een jaar ETS in Amsterdam.
Maar op die scool werd ik mentaal spuugziek van de meeste leraren, zo'n gigantisch verschil met de leraren op de 3e LTS.

Men vond dat men zich ondermeer met moderne wiskunde moest bezig houden, cirkels met puntjes er in, what the fuck, alles weggegooide tijd,
ik scheld nu iemand nog wel eens uit voor "lege verzameling" *grin* dat is het enige wat mij is bijgebleven.

Als ze meer praktische voorbeelden hadden gegeven met algebra, dan had ik daar nu veel meer aan gehad.
Wat ik wel goed heb meegekregen is het hoofdrekenen dat ik op de LTS heb geleerd, dagelijk heb ik daar nog profijt van.
Een flink deel heeft naturulijk te maken met mijn dyslexie, waar niemand in de 60 en 70 jaren van gehoord had.
Dan was er ook nog een gym leraar, die trots was op at hij in het leger had gezeten, en probeerde ons te drillen, wat een idioot!
Een Duits lerares die zij toen ik 17 jaar oud was, dat ik in de hoek moest gaan staan omdat ik niet stil genoeg was, toen ben ik maar naar huis gegaan.
In april of mei van het eerste jaar kon wat mij beteft die school de boom in.

Om een voorbeeld te geven hoe het wel goed kan wat leraar betreft is b.v. Christopher Lum
Hier legt hij een manier uit hoe je een P.I.D kan tunen, duilijk een bevlogen lerar die het mij veel makkelijker maakt iets te leren.
https://www.youtube.com/watch?v=n829SwSUZ_c

Nog een voor een P.I.D. tuning, deze is van: Northwestern Robotics
https://www.youtube.com/watch?v=uXnDwojRb1g

Twee verschillende manieren en beide goed, vooral als je ze samen bekijkt/leert.

Terug naar volledig on topic. :+
Ik wil het nog even over sprintf hebben.
Ja dat werkt goed in mijn voorbeeld code, het flits echt voorbij op de Teensy-4 zonder residu van de daarvoor aanwezige waarden op het display.
Maar waarom werkt het via sprintf wel tekst/cijfers zo snel aan te plaatsen en met b.v. tft.print(CounterWaarde); niet.

Ik heb even met een delay van 0,1 seconde in de loop getest en dan kan ik goed zien dat de sprintf truc goed werkt, geen ghosting van de gebruikte karakters.
Mooi, als je nu sprintf gebruikt, maak je dan een soort plaatje van de variabele die je in die functie stopt die dan in één keer naar in het display wordt geplaatst?

Groet,
Bram

You have your way. I have my way. As for the right way, the correct way, and the only way, it does not exist.

Ja zo zou je het kunnen zien:

code:


sprintf(TX,"%03d.%02d", intPart, decimalPart);

Dit levert een string op die altijd 6 letters lang is (3 cijfers, een punt en dan 2 cijfers). Die kun je dan direkt naar het scherm schrijven.

Bij tft.print() kun je niet opgeven hoeveel letters je wilt, en krijg je dus steeds een ander aantal, afhankelijk van het getal dat je print. En daardoor blijven er vaak een paar oude letters staan in het display. En dat wil je niet en dus moet je die weghalen door telkens eerst een rectangle te schrijven.

Dat kun je ook oplossen door na de tft.print() nog een paar extra spaties te printen. tft.print() retourneert het aantal letters dat er geschreven zijn, dus dan kun je uitrekenen hoeveel je tekort komt.

code:


   int n = tft.print(Frequency);
   while(n++ < 6)
   {  tft.print(" ");
   }

De eerste code had :

code:


    tft.setFont(&FreeMonoBoldOblique12pt7b);
    tft.setCursor(20, 135);
    tft.fillRect(21, 104 , 130, 45, 0xF800); 
    tft.setTextColor(ST77XX_YELLOW);
    tft.setTextSize(2);
    tft.print(newLeft);                     // Plaatst de Rotary encoder waarde op het scherm maar flikkert sterk

De tft.fillRect() haalt de tekst weg.
De tft.print() schrijft nieuwe tekst. Maar daar zit tijd tussen en dat zie je.

blackdog

Honourable Member

Hi deKees,

Jouw truc met houd nog geen rekening met de fonts die geen achtergrond hebben, zodat er bijna altijd residu van de vorige karakters achterblijft.
Dan wordt het lastg de goede font te vinden die bij het project passen en een anchtergrond hebben.

Dus grote kans dat ik jullie "sprintf(TX,"%03d.%02d", intPart, decimalPart);" ga gebruiken in mijn code.

Dank weer voor de input.

Bram

You have your way. I have my way. As for the right way, the correct way, and the only way, it does not exist.

Op 16 november 2020 10:22:05 schreef blackdog:
Voor mij is een perfecte log conversie niet echt nodig, als je kijkt ...

Klopt. Voor de "schaalverdeling" is het niet nodig dat die exact logarithmisch is. Mijn benadering met integers was al HEEL aardig en ruim voldoende. Maar goed.

Waar het er wel op aankomt is als je de conversie doet naar een print-baar getal voor op het display. Als je dan met log10 (999.9) = 2.99 dus het past in 3 cijfers gaat rekenen en dan de 999.9 afrond naar 1000 en in de geplande 3 plekjes probeert te stoppen dan heb je een probleem.

Op 16 november 2020 10:35:40 schreef deKees:
Maar het gaat hier over de range 10 tot 50000, dus dit probleem speelt hier helemaal niet.

Dat is niet waar. Want na 10 is de eerste volgende stap 10.015 o.i.d (toen met 15-bits regelbereik werd gewerkt). En wat we toen zagen is dat in de eerste "alleen integers" berekening er eerst een hele zwik 10,10,10... als output kwam. Het is wel degelijk wenselijk dat er dan iets aan de output verandert als je aan de knop draait.

four NANDS do make a NOR . Kijk ook eens in onze shop: http://www.bitwizard.nl/shop/

???

code:


if (f < 1000.0)
   sprintf(s,"%7.2f Hz ", Frequency);
else
   sprintf(s,"%7.4f kHz", Frequency/1000.);

@ deKees moet de tweede regel niet zijn

c code:

sprintf(s,"%4.4f kHz", Frequency/1000.)

Anders is de opmaak voor beide strings verschillende in lengte

[Bericht gewijzigd door benleentje op maandag 16 november 2020 15:01:46 (33%)

Mensen zijn soms net als een gelijkrichter, ze willen graag hun gelijk hebben.

@benleentje: Ik geloof dat die 7 steeds "totale ruimte" is. Dus als je die hetzelfde houdt

@dekees: Ik heb jou code in een loopje van 999 tot 1001 gedraaid.

Randcondities en off-by-one fouten zijn belangrijk!

code:

> 999.00 Hz <
> 999.10 Hz <
> 999.20 Hz <
> 999.30 Hz <
> 999.40 Hz <
> 999.50 Hz <
> 999.60 Hz <
> 999.70 Hz <
> 999.80 Hz <
> 999.90 Hz <
>1000.00 Hz <
> 1.0001 kHz<
> 1.0002 kHz<
> 1.0003 kHz<
> 1.0004 kHz<
> 1.0005 kHz<
> 1.0006 kHz<
> 1.0007 kHz<
> 1.0008 kHz<

Kennelijk genereert je code dus een extra spatie VOOR de 999 en de 1.0001 kHz. Ik zou verwachten dat je die NIET alleen bij precies 1000 Hz wil gebruiken.

Zelf proberen:

code:


#include <stdio.h>
#include <stdlib.h>

int main (int argc, char **argv)

{
  char s[64];
  float f;

  for (f=999;f<1001;f+=.1)  {
    if (f < 1000.0)
      sprintf(s,"%7.2f Hz ", f);
    else
      sprintf(s,"%7.4f kHz", f/1000.);
    printf (">%s<\n", s);
   }
  exit (0);
}

Edit: Als ik de += in de for met stapjes van een kwart of een achtste laat gaan dan is ie WEL precies 1000.0 en print ie 1.0000 kHz en niet 1000.00 Hz.

Edit2: Ohhh! Geinig! Als je stapjes van 0.2 doet, dan is de oneindige breuk afgerond naar boven en zit je dus BOVEN de 1000 op het moment dat je decimaal prcies op 1000 zit.

code:

> 1.0000 kHz< ... 1000.000061035156

Maar 0.1 is naar beneden afgerond en dan zit je net ONDER de werkelijke waarde:

code:

>1000.00 Hz < ... 999.999755859375

ook nog op te merken: Met stapjes 0.2 doet ie dus de 1000.8 als laatste (totaal 10 regels) terwijl met stapjes 0.1 dus de 1001.0 als laatste doet! niet precies het dubbele maar 21 regels output!

[Bericht gewijzigd door rew op maandag 16 november 2020 15:49:19 (19%)

four NANDS do make a NOR . Kijk ook eens in onze shop: http://www.bitwizard.nl/shop/

@rew:

Geinig. Komt natuurlijk doordat 0.1 feitelijk net iets minder is dan 0.1 (0.099999999). Dus je zou de grens moeten zetten op 999.999.

Die voorloop spatie is bewust. Die heb je nodig zodra je boven 10 kHz komt, en zo heb je altijd dezelfde lengte.

@benleentje:
Je brengt me aan het twijfelen. Volgens mij is het eerste getal de totale lengte, zoals rew ook al zei. In het eerste voorbeeld met integers zijn het 2 aparte getallen die dus elk een eigen lengte hebben.

Maar voor de zekerheid heb ik het even geprobeerd, en dan blijkt dat Arduino de floating point support voor printf heeft uitgeschakeld (voor mijn UNO tenminste). Dus dan moet je weer terugvallen op dtostrf()

code:


char Buffer[20];
if (Frequentie < 999.999)
   dtostrf(Frequency, 7, 2, Buffer);
else
   dtostrf(Frequency/1000, 7, 4, Buffer);